Compare commits
43 Commits
v4.8.10-al
...
v4.8.10-fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02bf400bf3 | ||
|
|
11cbcca2d4 | ||
|
|
34422f9549 | ||
|
|
aeba79267a | ||
|
|
7473be5922 | ||
|
|
de59b3d2e5 | ||
|
|
08190c2f0d | ||
|
|
6a85c8c2b6 | ||
|
|
bbdab1d40e | ||
|
|
78ad2791cd | ||
|
|
5f3c8e9046 | ||
|
|
30057f01a6 | ||
|
|
3ea185315d | ||
|
|
a1ae08f62b | ||
|
|
91ec895fd2 | ||
|
|
1a33642635 | ||
|
|
e9681c8ed5 | ||
|
|
69ff65973f | ||
|
|
52ac445557 | ||
|
|
d45cb2f84a | ||
|
|
1cb71c6bfb | ||
|
|
fb59b60761 | ||
|
|
9334a0dcf6 | ||
|
|
c614f8b9ca | ||
|
|
478386c612 | ||
|
|
dfcffc7fc1 | ||
|
|
b4238257b6 | ||
|
|
38f47956cd | ||
|
|
7fed4d697f | ||
|
|
5ed89130ef | ||
|
|
3671e55001 | ||
|
|
3bcc3430fb | ||
|
|
d6233cd7b1 | ||
|
|
64708ea424 | ||
|
|
85a11d08b2 | ||
|
|
a7569037fe | ||
|
|
4726034344 | ||
|
|
9a57e94b79 | ||
|
|
761e35c226 | ||
|
|
5ebe0017a0 | ||
|
|
036097243a | ||
|
|
84de95d294 | ||
|
|
fdab383b26 |
BIN
.github/imgs/intro1.png
vendored
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 173 KiB |
BIN
.github/imgs/intro2.png
vendored
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 273 KiB |
BIN
.github/imgs/intro3.png
vendored
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 168 KiB |
BIN
.github/imgs/intro4.png
vendored
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 159 KiB |
2
.vscode/settings.json
vendored
@@ -13,7 +13,7 @@
|
||||
"js",
|
||||
"ts"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.keystyle": "flat",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": false,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
|
||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 298 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 119 KiB |
@@ -62,6 +62,23 @@ weight: 113
|
||||
34.142.157.52
|
||||
```
|
||||
|
||||
国内版用户(fastgpt.cn)可以填写下面的 IP 白名单:
|
||||
|
||||
```
|
||||
47.97.59.172
|
||||
121.43.108.48
|
||||
121.41.75.88
|
||||
121.41.178.7
|
||||
121.40.65.187
|
||||
121.196.235.183
|
||||
120.55.195.90
|
||||
120.55.193.112
|
||||
120.26.229.115
|
||||
112.124.41.79
|
||||
101.37.205.32
|
||||
47.98.190.173
|
||||
```
|
||||
|
||||
## 4. 获取AES Key,选择加密方式
|
||||
|
||||

|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.8.10(进行中)'
|
||||
title: 'V4.8.10'
|
||||
description: 'FastGPT V4.8.10 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -24,8 +24,8 @@ STORE_LOG_LEVEL=warn
|
||||
|
||||
### 3. 修改镜像tag
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.8.10-alpha
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.8.10-alpha
|
||||
- 更新 FastGPT 镜像 tag: v4.8.10-fix
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.8.10-fix
|
||||
- Sandbox 镜像,可以不更新
|
||||
|
||||
## 4. 执行初始化
|
||||
@@ -45,40 +45,60 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
|
||||
|
||||
## V4.8.10 更新说明
|
||||
|
||||
完整内容请见:[4.8.10 release](https://github.com/labring/FastGPT/releases/tag/v4.8.10-alpha)
|
||||
完整内容请见:[4.8.10 release](https://github.com/labring/FastGPT/releases/tag/v4.8.10)
|
||||
|
||||
1. 新增 - 模板市场
|
||||
2. 新增 - 工作流节点拖动自动对齐吸附
|
||||
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
||||
4. 新增 - 工作流撤销和重做
|
||||
5. 新增 - 工作流本次编辑记录,取代自动保存
|
||||
6. 新增 - 工作流版本支持重命名
|
||||
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
|
||||
8. 新增 - 插件增加使用说明配置。
|
||||
9. 商业版新增 - 飞书机器人接入
|
||||
10. 商业版新增 - 公众号接入接入
|
||||
11. 商业版新增 - 自助开票申请
|
||||
12. 商业版新增 - SSO 定制
|
||||
13. 优化 - SSE 响应优化。
|
||||
14. 优化 - 无 SSL 证书情况下,优化复制。
|
||||
15. 优化 - 单选框打开后自动滚动到选中的位置。
|
||||
16. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
||||
17. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
||||
18. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
||||
19. 优化 - 知识库列表 UI。
|
||||
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 模式下,循环调用边问题。
|
||||
1. 新增 - 模板市场。
|
||||
2. 新增 - 工作流节点拖动自动对齐吸附。
|
||||
3. 新增 - 用户选择节点(Debug 模式暂未支持)。
|
||||
4. 新增 - 工作流增加 uid 全局变量。
|
||||
5. 新增 - 工作流撤销和重做。
|
||||
6. 新增 - 工作流本次编辑记录,取代自动保存。
|
||||
7. 新增 - 工作流版本支持重命名。
|
||||
8. 新增 - 工作流的“应用调用”节点弃用,迁移成单独节点,与插件使用方式相同,同时可以传递全局变量和用户上传的文件。
|
||||
9. 新增 - 插件增加使用说明配置。
|
||||
10. 新增 - 插件自定义输入支持单选框。
|
||||
11. 新增 - HTTP 节点支持 text/plain 模式。
|
||||
12. 新增 - HTTP模块支持超时配置、支持更多的 Body 类型,params 和 headers 支持新的变量选择模式。
|
||||
13. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流。
|
||||
14. 新增 - 发送验证码安全校验。
|
||||
15. 商业版新增 - 飞书机器人接入。
|
||||
16. 商业版新增 - 公众号接入接入。
|
||||
17. 商业版新增 - 自助开票申请。
|
||||
18. 商业版新增 - SSO 定制。
|
||||
19. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行。
|
||||
20. 优化 - 工作流嵌套执行,参数可能存在的污染问题。
|
||||
21. 优化 - 部分全局变量,增加数据类型约束。
|
||||
22. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
||||
23. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
||||
24. 优化 - 对话框性能问题。
|
||||
25. 优化 - 单选框打开后自动滚动到选中的位置。
|
||||
26. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
||||
27. 优化 - SSE 响应代码优化。
|
||||
28. 优化 - 无 SSL 证书情况下,优化复制。
|
||||
29. 优化 - 知识库列表 UI。
|
||||
30. 优化 - 知识库详情页 UI。
|
||||
31. 优化 - 支持无网络配置情况下运行。
|
||||
32. 优化 - 调整.env.template关于mongodb的说明,使得更易于理解。
|
||||
33. 优化 - 新的支付模式。
|
||||
34. 优化 - 用户默认头像。
|
||||
35. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
||||
36. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
||||
37. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
||||
38. 修复 - 创建 APP 副本,无法复制系统配置。
|
||||
39. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
||||
40. 修复 - 内容提取的数据类型与输出数据类型未一致。
|
||||
41. 修复 - 工作流运行时间统计错误。
|
||||
42. 修复 - stream 模式下,工具调用有可能出现 undefined。
|
||||
43. 修复 - reranker typo。
|
||||
44. 修复 - home host typo。
|
||||
45. 修复 - i18n display。
|
||||
46. 修复 - 全局变量可重复定义 key。
|
||||
47. 修复 - 全局变量在 Debug 模式下不可持久化。
|
||||
48. 修复 - 全局变量在 API 中无法持久化。
|
||||
49. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)。
|
||||
50. 修复 - 知识库标签重复加载。
|
||||
51. 修复 - 网络链接重新获取时,自定义分割符不生效。
|
||||
52. 修复 - 插件运行时,会传递额外的全局变量,可能造成插件内变量污染。
|
||||
53. 文档 - qa docs。
|
||||
54. 文档 - Update feishu.md。
|
||||
55. 文档 - update baseURL。
|
||||
|
||||
@@ -16,8 +16,13 @@ weight: 813
|
||||
## V4.8.11 更新预告
|
||||
|
||||
1.
|
||||
2. 新增 - 插件自定义输入支持单选框
|
||||
3. 新增 - 插件输出,支持指定某些字段为工具调用结果
|
||||
4. 新增 - 插件支持配置使用引导、全局变量和文件输入
|
||||
5. 优化 - SSE 响应代码。
|
||||
6. 优化 - 非 HTTPS 环境下支持复制(除非 textarea 复制也不支持)
|
||||
2. 新增 - 工作流循环执行节点。
|
||||
3. 新增 - 工作流用户表单输入节点。
|
||||
4. 新增 - 插件输出,支持指定某些字段为工具调用结果。
|
||||
5. 新增 - 插件支持配置使用引导、全局变量和文件输入。
|
||||
6. 新增 - 简易模式支持新的版本管理方式。
|
||||
7. 新增 - 聊天记录滚动加载,不再只加载 30 条。
|
||||
8. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。
|
||||
9. 优化 - 工作流 handler 性能优化。
|
||||
10. 修复 - 知识库选择权限问题。
|
||||
11. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。
|
||||
|
||||
0
fastgpt@4.0
Normal file
@@ -10,8 +10,7 @@
|
||||
"postinstall": "sh ./scripts/postinstall.sh",
|
||||
"initIcon": "node ./scripts/icon/init.js",
|
||||
"previewIcon": "node ./scripts/icon/index.js",
|
||||
"i18n:delete-unused-keys": "node ./scripts/i18n/delete-unused-keys.js",
|
||||
"i18n:query": "node ./scripts/i18n/query.js"
|
||||
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/cli": "^2.4.1",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { PromptTemplateItem } from '../type.d';
|
||||
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: '标准模板',
|
||||
desc: '标准提示词,用于结构不固定的知识库。',
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: i18nT('app:template.standard_template_des'),
|
||||
value: `{{q}}
|
||||
{{a}}`
|
||||
},
|
||||
{
|
||||
title: '问答模板',
|
||||
desc: '适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答',
|
||||
title: i18nT('app:template.qa_template'),
|
||||
desc: i18nT('app:template.qa_template_des'),
|
||||
value: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
@@ -18,14 +18,14 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
</Answer>`
|
||||
},
|
||||
{
|
||||
title: '标准严格模板',
|
||||
desc: '在标准模板基础上,对模型的回答做更严格的要求。',
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: i18nT('app:template.standard_strict_des'),
|
||||
value: `{{q}}
|
||||
{{a}}`
|
||||
},
|
||||
{
|
||||
title: '严格问答模板',
|
||||
desc: '在问答模板基础上,对模型的回答做更严格的要求。',
|
||||
title: i18nT('app:template.hard_strict'),
|
||||
desc: i18nT('app:template.hard_strict_des'),
|
||||
value: `<Question>
|
||||
{{q}}
|
||||
</Question>
|
||||
@@ -37,7 +37,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
|
||||
export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
{
|
||||
title: '标准模板',
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: '',
|
||||
value: `使用 <Data></Data> 标记中的内容作为你的知识:
|
||||
|
||||
@@ -55,7 +55,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '问答模板',
|
||||
title: i18nT('app:template.qa_template'),
|
||||
desc: '',
|
||||
value: `使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
@@ -72,7 +72,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '标准严格模板',
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <Data></Data> 标记中的内容作为你的知识:
|
||||
|
||||
@@ -94,7 +94,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
问题:"""{{question}}"""`
|
||||
},
|
||||
{
|
||||
title: '严格问答模板',
|
||||
title: i18nT('app:template.hard_strict'),
|
||||
desc: '',
|
||||
value: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export const chats2GPTMessages = ({
|
||||
tool_calls
|
||||
})
|
||||
.concat(toolResponse);
|
||||
} else if (value.text) {
|
||||
} else if (value.text?.content) {
|
||||
results.push({
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
@@ -142,7 +142,7 @@ export const GPTMessages2Chats = (
|
||||
messages: ChatCompletionMessageParam[],
|
||||
reserveTool = true
|
||||
): ChatItemType[] => {
|
||||
return messages
|
||||
const chatMessages = messages
|
||||
.map((item) => {
|
||||
const value: ChatItemType['value'] = [];
|
||||
const obj = GPT2Chat[item.role];
|
||||
@@ -151,12 +151,23 @@ export const GPTMessages2Chats = (
|
||||
obj === ChatRoleEnum.System &&
|
||||
item.role === ChatCompletionRequestMessageRoleEnum.System
|
||||
) {
|
||||
value.push({
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: item.content
|
||||
}
|
||||
});
|
||||
if (Array.isArray(item.content)) {
|
||||
item.content.forEach((item) => [
|
||||
value.push({
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: item.text
|
||||
}
|
||||
})
|
||||
]);
|
||||
} else {
|
||||
value.push({
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: item.content
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
obj === ChatRoleEnum.Human &&
|
||||
item.role === ChatCompletionRequestMessageRoleEnum.User
|
||||
@@ -277,6 +288,22 @@ export const GPTMessages2Chats = (
|
||||
} as ChatItemType;
|
||||
})
|
||||
.filter((item) => item.value.length > 0);
|
||||
|
||||
// Merge data with the same dataId
|
||||
const result = chatMessages.reduce((result: ChatItemType[], currentItem) => {
|
||||
const lastItem = result[result.length - 1];
|
||||
|
||||
if (lastItem && lastItem.dataId === currentItem.dataId && lastItem.obj === currentItem.obj) {
|
||||
// @ts-ignore
|
||||
lastItem.value = lastItem.value.concat(currentItem.value);
|
||||
} else {
|
||||
result.push(currentItem);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): RuntimeUserPromptType => {
|
||||
|
||||
@@ -108,6 +108,8 @@ export enum NodeInputKeyEnum {
|
||||
httpMethod = 'system_httpMethod',
|
||||
httpParams = 'system_httpParams',
|
||||
httpJsonBody = 'system_httpJsonBody',
|
||||
httpFormBody = 'system_httpFormBody',
|
||||
httpContentType = 'system_httpContentType',
|
||||
httpTimeout = 'system_httpTimeout',
|
||||
abandon_httpUrl = 'url',
|
||||
|
||||
@@ -217,3 +219,13 @@ export enum RuntimeEdgeStatusEnum {
|
||||
|
||||
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
|
||||
export const DYNAMIC_INPUT_REFERENCE_KEY = 'DYNAMIC_INPUT_REFERENCE_KEY';
|
||||
|
||||
// http node body content type
|
||||
export enum ContentTypes {
|
||||
none = 'none',
|
||||
formData = 'form-data',
|
||||
xWwwFormUrlencoded = 'x-www-form-urlencoded',
|
||||
json = 'json',
|
||||
xml = 'xml',
|
||||
raw = 'raw-text'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WorkflowIOValueTypeEnum } from '../constants';
|
||||
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
export enum FlowNodeInputTypeEnum { // render ui
|
||||
reference = 'reference', // reference to other node output
|
||||
input = 'input', // one line input
|
||||
@@ -167,23 +167,23 @@ export const FlowValueTypeMap = {
|
||||
value: WorkflowIOValueTypeEnum.any
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.chatHistory]: {
|
||||
label: '历史记录',
|
||||
label: i18nT('common:core.chat.History'),
|
||||
value: WorkflowIOValueTypeEnum.chatHistory
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.datasetQuote]: {
|
||||
label: '知识库引用',
|
||||
label: i18nT('common:core.workflow.Dataset quote'),
|
||||
value: WorkflowIOValueTypeEnum.datasetQuote
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.selectApp]: {
|
||||
label: '选择应用',
|
||||
label: i18nT('common:plugin.App'),
|
||||
value: WorkflowIOValueTypeEnum.selectApp
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.selectDataset]: {
|
||||
label: '选择知识库',
|
||||
label: i18nT('common:core.chat.Select dataset'),
|
||||
value: WorkflowIOValueTypeEnum.selectDataset
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.dynamic]: {
|
||||
label: '动态输入',
|
||||
label: i18nT('common:core.workflow.dynamic_input'),
|
||||
value: WorkflowIOValueTypeEnum.dynamic
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,8 +22,10 @@ export enum DispatchNodeResponseKeyEnum {
|
||||
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
|
||||
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
|
||||
assistantResponses = 'assistantResponses', // assistant response
|
||||
rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite
|
||||
|
||||
interactive = 'INTERACTIVE' // is interactive
|
||||
interactive = 'INTERACTIVE', // is interactive
|
||||
runTimes = 'runTimes' // run times
|
||||
}
|
||||
|
||||
export const needReplaceReferenceInputTypeList = [
|
||||
|
||||
26
packages/global/core/workflow/runtime/type.d.ts
vendored
@@ -2,9 +2,9 @@ import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
|
||||
import {
|
||||
ChatItemType,
|
||||
UserChatItemValueItemType,
|
||||
ChatItemValueItemType,
|
||||
ToolRunResponseItemType,
|
||||
NodeOutputItemType
|
||||
NodeOutputItemType,
|
||||
AIChatItemValueItemType
|
||||
} from '../../chat/type';
|
||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
|
||||
import { StoreNodeItemType } from '../type/node';
|
||||
@@ -26,10 +26,15 @@ export type ChatDispatchProps = {
|
||||
res?: NextApiResponse;
|
||||
requestOrigin?: string;
|
||||
mode: 'test' | 'chat' | 'debug';
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
user: UserModelSchema;
|
||||
app: AppDetailType | AppSchema;
|
||||
|
||||
runningAppInfo: {
|
||||
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)
|
||||
teamId: string;
|
||||
tmbId: string; // App tmbId
|
||||
};
|
||||
uid: string; // Who run this workflow
|
||||
|
||||
chatId?: string;
|
||||
responseChatItemId?: string;
|
||||
histories: ChatItemType[];
|
||||
@@ -40,6 +45,7 @@ export type ChatDispatchProps = {
|
||||
maxRunTimes: number;
|
||||
isToolCall?: boolean;
|
||||
workflowStreamResponse?: WorkflowResponseType;
|
||||
workflowDispatchDeep?: number;
|
||||
};
|
||||
|
||||
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||
@@ -50,10 +56,12 @@ export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||
};
|
||||
|
||||
export type SystemVariablesType = {
|
||||
userId: string;
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
responseChatItemId?: string;
|
||||
histories: ChatItemType[];
|
||||
cTime: string;
|
||||
};
|
||||
|
||||
/* node props */
|
||||
@@ -69,7 +77,7 @@ export type RuntimeNodeItemType = {
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
|
||||
pluginId?: string;
|
||||
pluginId?: string; // workflow id / plugin id
|
||||
};
|
||||
|
||||
export type PluginRuntimeType = {
|
||||
@@ -166,13 +174,15 @@ export type DispatchNodeResponseType = {
|
||||
updateVarResult?: any[];
|
||||
};
|
||||
|
||||
export type DispatchNodeResultType<T> = {
|
||||
export type DispatchNodeResultType<T = {}> = {
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]?: string[]; // skip some edge handle id
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
|
||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; // Node total usage
|
||||
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[]; // Children node response
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType; // Tool response
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[]; // Assistant response(Store to db)
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // Assistant response(Store to db)
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]?: ChatItemType[];
|
||||
[DispatchNodeResponseKeyEnum.runTimes]?: number;
|
||||
} & T;
|
||||
|
||||
/* Single node props */
|
||||
|
||||
@@ -27,16 +27,32 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
|
||||
return limit * 2;
|
||||
};
|
||||
|
||||
/*
|
||||
Get interaction information (if any) from the last AI message.
|
||||
What can be done:
|
||||
1. Get the interactive data
|
||||
2. Check that the workflow starts at the interaction node
|
||||
*/
|
||||
export const getLastInteractiveValue = (histories: ChatItemType[]) => {
|
||||
const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI);
|
||||
|
||||
if (lastAIMessage) {
|
||||
const interactiveValue = lastAIMessage.value.find(
|
||||
(v) => v.type === ChatItemValueTypeEnum.interactive
|
||||
);
|
||||
const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1];
|
||||
|
||||
if (interactiveValue && 'interactive' in interactiveValue) {
|
||||
return interactiveValue.interactive;
|
||||
if (
|
||||
!lastValue ||
|
||||
lastValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!lastValue.interactive
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check is user select
|
||||
if (
|
||||
lastValue.interactive.type === 'userSelect' &&
|
||||
!lastValue.interactive.params.userSelectedVal
|
||||
) {
|
||||
return lastValue.interactive;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { RunAppModule } from './system/abandoned/runApp/index';
|
||||
import { PluginInputModule } from './system/pluginInput';
|
||||
import { PluginOutputModule } from './system/pluginOutput';
|
||||
import { RunPluginModule } from './system/runPlugin';
|
||||
import { RunAppPluginModule } from './system/runAppPlugin';
|
||||
import { RunAppNode } from './system/runApp';
|
||||
import { AiQueryExtension } from './system/queryExtension';
|
||||
|
||||
import type { FlowNodeTemplateType } from '../type/node';
|
||||
@@ -73,6 +73,6 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||
),
|
||||
EmptyNode,
|
||||
RunPluginModule,
|
||||
RunAppPluginModule,
|
||||
RunAppNode,
|
||||
RunAppModule
|
||||
];
|
||||
|
||||
@@ -51,15 +51,15 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
||||
label: '',
|
||||
value: [
|
||||
{
|
||||
value: i18nT('workflow:greeting'),
|
||||
value: 'Greeting',
|
||||
key: 'wqre'
|
||||
},
|
||||
{
|
||||
value: i18nT('workflow:about_xxx_question'),
|
||||
value: 'Question regarding xxx',
|
||||
key: 'sdfa'
|
||||
},
|
||||
{
|
||||
value: i18nT('workflow:other_questions'),
|
||||
value: 'Other Questions',
|
||||
key: 'agex'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
WorkflowIOValueTypeEnum,
|
||||
NodeInputKeyEnum,
|
||||
NodeOutputKeyEnum,
|
||||
FlowNodeTemplateTypeEnum
|
||||
FlowNodeTemplateTypeEnum,
|
||||
ContentTypes
|
||||
} from '../../constants';
|
||||
import { Input_Template_DynamicInput } from '../input';
|
||||
import { Output_Template_AddOutput } from '../output';
|
||||
@@ -82,6 +83,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
label: '',
|
||||
required: false
|
||||
},
|
||||
// json body data
|
||||
{
|
||||
key: NodeInputKeyEnum.httpJsonBody,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
@@ -89,6 +91,24 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
value: '',
|
||||
label: '',
|
||||
required: false
|
||||
},
|
||||
// form body data
|
||||
{
|
||||
key: NodeInputKeyEnum.httpFormBody,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
value: [],
|
||||
label: '',
|
||||
required: false
|
||||
},
|
||||
// body data type
|
||||
{
|
||||
key: NodeInputKeyEnum.httpContentType,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
value: ContentTypes.json,
|
||||
label: '',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
|
||||
@@ -3,7 +3,7 @@ import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../type/node';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const RunAppPluginModule: FlowNodeTemplateType = {
|
||||
export const RunAppNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.appModule,
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
flowNodeType: FlowNodeTypeEnum.appModule,
|
||||
@@ -15,7 +15,7 @@ type InteractiveBasicType = {
|
||||
type UserSelectInteractive = {
|
||||
type: 'userSelect';
|
||||
params: {
|
||||
// description: string;
|
||||
description: string;
|
||||
userSelectOptions: UserSelectOptionItemType[];
|
||||
userSelectedVal?: string;
|
||||
};
|
||||
|
||||
@@ -326,27 +326,6 @@ export const updatePluginInputByVariables = (
|
||||
);
|
||||
};
|
||||
|
||||
/* Remove pluginInput variables from global variables
|
||||
(completions api: Plugin input get value from global variables)
|
||||
*/
|
||||
export const removePluginInputVariables = (
|
||||
variables: Record<string, any>,
|
||||
nodes: RuntimeNodeItemType[]
|
||||
) => {
|
||||
const pluginInputNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
|
||||
|
||||
if (!pluginInputNode) return variables;
|
||||
return Object.keys(variables).reduce(
|
||||
(acc, key) => {
|
||||
if (!pluginInputNode.inputs.find((input) => input.key === key)) {
|
||||
acc[key] = variables[key];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
);
|
||||
};
|
||||
|
||||
// replace {{$xx.xx$}} variables for text
|
||||
export function replaceEditorVariable({
|
||||
text,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"jschardet": "3.1.1",
|
||||
"nanoid": "^4.0.1",
|
||||
"next": "14.2.5",
|
||||
"openai": "4.53.0",
|
||||
"openai": "4.57.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"timezones-list": "^3.0.2"
|
||||
},
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
||||
import { PermissionListType } from '../type';
|
||||
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
export enum AppPermissionKeyEnum {}
|
||||
export const AppPermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
...PermissionList[PermissionKeyEnum.read],
|
||||
description: '可使用该应用进行对话'
|
||||
description: i18nT('app:permission.des.read')
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
...PermissionList[PermissionKeyEnum.write],
|
||||
description: '可查看和编辑应用'
|
||||
description: i18nT('app:permission.des.write')
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限'
|
||||
description: i18nT('app:permission.des.manage')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Permission } from './controller';
|
||||
import { PermissionListType } from './type';
|
||||
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
export enum AuthUserTypeEnum {
|
||||
token = 'token',
|
||||
root = 'root',
|
||||
@@ -27,15 +27,15 @@ export const PermissionTypeMap = {
|
||||
},
|
||||
[PermissionTypeEnum.publicRead]: {
|
||||
iconLight: 'support/permission/publicLight',
|
||||
label: '团队可访问'
|
||||
label: i18nT('user:permission.team_read')
|
||||
},
|
||||
[PermissionTypeEnum.publicWrite]: {
|
||||
iconLight: 'support/permission/publicLight',
|
||||
label: '团队可编辑'
|
||||
label: i18nT('user:permission.team_write')
|
||||
},
|
||||
[PermissionTypeEnum.clbPrivate]: {
|
||||
iconLight: 'support/permission/privateLight',
|
||||
label: '仅协作者'
|
||||
label: i18nT('user:permission.only_collaborators')
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,19 +53,19 @@ export enum PermissionKeyEnum {
|
||||
}
|
||||
export const PermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
name: '读权限',
|
||||
name: i18nT('common:permission.read'),
|
||||
description: '',
|
||||
value: 0b100,
|
||||
checkBoxType: 'single'
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
name: '写权限',
|
||||
name: i18nT('common:permission.write'),
|
||||
description: '',
|
||||
value: 0b110, // 如果某个资源有特殊要求,再重写这个值
|
||||
checkBoxType: 'single'
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
name: '管理员',
|
||||
name: i18nT('common:permission.manager'),
|
||||
description: '',
|
||||
value: 0b111,
|
||||
checkBoxType: 'single'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
||||
|
||||
export enum DatasetPermissionKeyEnum {}
|
||||
@@ -5,15 +6,15 @@ export enum DatasetPermissionKeyEnum {}
|
||||
export const DatasetPermissionList = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
...PermissionList[PermissionKeyEnum.read],
|
||||
description: '可查看知识库内容'
|
||||
description: i18nT('dataset:permission.des.read')
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
...PermissionList[PermissionKeyEnum.write],
|
||||
description: '可增加和变更知识库内容'
|
||||
description: i18nT('dataset:permission.des.write')
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '可管理整个知识库数据和信息'
|
||||
description: i18nT('dataset:permission.des.manage')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { PermissionKeyEnum, PermissionList, ReadPermissionVal } from '../constant';
|
||||
import { PermissionListType } from '../type';
|
||||
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
export const TeamPermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
...PermissionList[PermissionKeyEnum.read],
|
||||
description: '成员仅可阅读相关资源,无法新建资源'
|
||||
description: i18nT('user:permission_des.read')
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
...PermissionList[PermissionKeyEnum.write],
|
||||
description: '除了可读资源外,还可以新建新的资源'
|
||||
description: i18nT('user:permission_des.write')
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '可创建资源、邀请、删除成员'
|
||||
description: i18nT('user:permission_des.manage')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@ export enum UserAuthTypeEnum {
|
||||
register = 'register',
|
||||
findPassword = 'findPassword',
|
||||
wxLogin = 'wxLogin',
|
||||
bindNotification = 'bindNotification'
|
||||
bindNotification = 'bindNotification',
|
||||
captcha = 'captcha'
|
||||
}
|
||||
|
||||
export const userAuthTypeMap = {
|
||||
[UserAuthTypeEnum.register]: 'register',
|
||||
[UserAuthTypeEnum.findPassword]: 'findPassword',
|
||||
[UserAuthTypeEnum.wxLogin]: 'wxLogin',
|
||||
[UserAuthTypeEnum.bindNotification]: 'bindNotification'
|
||||
[UserAuthTypeEnum.bindNotification]: 'bindNotification',
|
||||
[UserAuthTypeEnum.captcha]: 'captcha'
|
||||
};
|
||||
|
||||
@@ -14,13 +14,13 @@ export enum UsageSourceEnum {
|
||||
|
||||
export const UsageSourceMap = {
|
||||
[UsageSourceEnum.fastgpt]: {
|
||||
label: '在线使用'
|
||||
label: i18nT('common:core.chat.logs.online')
|
||||
},
|
||||
[UsageSourceEnum.api]: {
|
||||
label: 'Api'
|
||||
},
|
||||
[UsageSourceEnum.shareLink]: {
|
||||
label: '免登录链接'
|
||||
label: i18nT('common:core.chat.logs.free_login')
|
||||
},
|
||||
[UsageSourceEnum.training]: {
|
||||
label: 'dataset.Training Name'
|
||||
|
||||
@@ -101,3 +101,5 @@ export const urlsFetch = async ({
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const loadContentByCheerio = async (content: string) => cheerio.load(content);
|
||||
|
||||
@@ -48,7 +48,7 @@ export class PgVectorCtrl {
|
||||
const { teamId, datasetId, collectionId, vector, retry = 3 } = props;
|
||||
|
||||
try {
|
||||
const { rows } = await PgClient.insert(DatasetVectorTableName, {
|
||||
const { rowCount, rows } = await PgClient.insert(DatasetVectorTableName, {
|
||||
values: [
|
||||
[
|
||||
{ key: 'vector', value: `[${vector}]` },
|
||||
@@ -58,6 +58,11 @@ export class PgVectorCtrl {
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
if (rowCount === 0) {
|
||||
return Promise.reject('insertDatasetData: no insert');
|
||||
}
|
||||
|
||||
return {
|
||||
insertId: rows[0].id
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getAIApi } from '../config';
|
||||
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
||||
import { loadRequestMessages } from '../../chat/utils';
|
||||
|
||||
export const Prompt_QuestionGuide = `你是一个AI智能助手,可以回答和解决我的问题。请结合前面的对话记录,帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
|
||||
export const Prompt_QuestionGuide = `你是一个AI智能助手,可以回答和解决我的问题。请结合前面的对话记录,帮我生成 3 个问题,引导我继续提问,生成问题的语言要与原问题相同。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
|
||||
|
||||
export async function createQuestionGuide({
|
||||
messages,
|
||||
@@ -19,6 +19,7 @@ export async function createQuestionGuide({
|
||||
content: Prompt_QuestionGuide
|
||||
}
|
||||
];
|
||||
|
||||
const ai = getAIApi({
|
||||
timeout: 480000
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { MongoChatItem } from './chatItemSchema';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { MongoChat } from './chatSchema';
|
||||
@@ -80,52 +80,6 @@ export const addCustomFeedbacks = async ({
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Update the user selected index of the interactive module
|
||||
*/
|
||||
export const updateUserSelectedResult = async ({
|
||||
appId,
|
||||
chatId,
|
||||
userSelectedVal
|
||||
}: {
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
userSelectedVal: string;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
try {
|
||||
const chatItem = await MongoChatItem.findOne(
|
||||
{ appId, chatId, obj: ChatRoleEnum.AI },
|
||||
'value'
|
||||
).sort({ _id: -1 });
|
||||
|
||||
if (!chatItem) return;
|
||||
|
||||
const interactiveValue = chatItem.value.find(
|
||||
(v) => v.type === ChatItemValueTypeEnum.interactive
|
||||
);
|
||||
|
||||
if (
|
||||
!interactiveValue ||
|
||||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!interactiveValue.interactive?.params
|
||||
)
|
||||
return;
|
||||
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal
|
||||
}
|
||||
};
|
||||
|
||||
await chatItem.save();
|
||||
} catch (error) {
|
||||
addLog.error('updateUserSelectedResult error', error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Delete chat files
|
||||
1. ChatId: Delete one chat files
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { MongoApp } from '../app/schema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
ChatItemValueTypeEnum,
|
||||
ChatRoleEnum,
|
||||
ChatSourceEnum
|
||||
} from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoChatItem } from './chatItemSchema';
|
||||
import { MongoChat } from './chatSchema';
|
||||
import { addLog } from '../../common/system/log';
|
||||
@@ -111,3 +115,85 @@ export async function saveChat({
|
||||
addLog.error(`update chat history error`, error);
|
||||
}
|
||||
}
|
||||
|
||||
export const updateInteractiveChat = async ({
|
||||
chatId,
|
||||
appId,
|
||||
teamId,
|
||||
tmbId,
|
||||
userSelectedVal,
|
||||
aiResponse,
|
||||
newVariables,
|
||||
newTitle
|
||||
}: {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
userSelectedVal: string;
|
||||
aiResponse: AIChatItemType & { dataId?: string };
|
||||
newVariables?: Record<string, any>;
|
||||
newTitle: string;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
|
||||
const chatItem = await MongoChatItem.findOne({ appId, chatId, obj: ChatRoleEnum.AI }).sort({
|
||||
_id: -1
|
||||
});
|
||||
|
||||
if (!chatItem || chatItem.obj !== ChatRoleEnum.AI) return;
|
||||
|
||||
const interactiveValue = chatItem.value[chatItem.value.length - 1];
|
||||
|
||||
if (
|
||||
!interactiveValue ||
|
||||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!interactiveValue.interactive?.params
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal
|
||||
}
|
||||
};
|
||||
|
||||
if (aiResponse.customFeedbacks) {
|
||||
chatItem.customFeedbacks = chatItem.customFeedbacks
|
||||
? [...chatItem.customFeedbacks, ...aiResponse.customFeedbacks]
|
||||
: aiResponse.customFeedbacks;
|
||||
}
|
||||
|
||||
if (aiResponse.responseData) {
|
||||
chatItem.responseData = chatItem.responseData
|
||||
? [...chatItem.responseData, ...aiResponse.responseData]
|
||||
: aiResponse.responseData;
|
||||
}
|
||||
|
||||
if (aiResponse.value) {
|
||||
chatItem.value = chatItem.value ? [...chatItem.value, ...aiResponse.value] : aiResponse.value;
|
||||
}
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
await chatItem.save({ session });
|
||||
await MongoChat.updateOne(
|
||||
{
|
||||
appId,
|
||||
chatId
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
variables: newVariables,
|
||||
title: newTitle,
|
||||
updateTime: new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
session
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -121,46 +121,22 @@ export const loadRequestMessages = async ({
|
||||
const imageRegex =
|
||||
/(https?:\/\/[^\s/$.?#].[^\s]*\.(?:png|jpe?g|gif|webp|bmp|tiff?|svg|ico|heic|avif))/i;
|
||||
|
||||
const result: { type: 'text' | 'image'; value: string }[] = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
const result: ChatCompletionContentPart[] = [];
|
||||
|
||||
// 使用正则表达式查找所有匹配项
|
||||
while ((match = imageRegex.exec(input.slice(lastIndex))) !== null) {
|
||||
const textBefore = input.slice(lastIndex, lastIndex + match.index);
|
||||
|
||||
// 如果图片URL前有文本,添加文本部分
|
||||
if (textBefore) {
|
||||
result.push({ type: 'text', value: textBefore });
|
||||
}
|
||||
|
||||
// 添加图片URL
|
||||
result.push({ type: 'image', value: match[0] });
|
||||
|
||||
lastIndex += match.index + match[0].length;
|
||||
}
|
||||
|
||||
// 添加剩余的文本(如果有的话)
|
||||
if (lastIndex < input.length) {
|
||||
result.push({ type: 'text', value: input.slice(lastIndex) });
|
||||
}
|
||||
|
||||
return result
|
||||
.map((item) => {
|
||||
if (item.type === 'text') {
|
||||
return { type: 'text', text: item.value };
|
||||
// 提取所有HTTPS图片URL并添加到result开头
|
||||
const httpsImages = input.match(imageRegex) || [];
|
||||
httpsImages.forEach((url) => {
|
||||
result.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: url
|
||||
}
|
||||
if (item.type === 'image') {
|
||||
return {
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: item.value
|
||||
}
|
||||
};
|
||||
}
|
||||
return { type: 'text', text: item.value };
|
||||
})
|
||||
.filter(Boolean) as ChatCompletionContentPart[];
|
||||
});
|
||||
});
|
||||
|
||||
// 添加原始input作为文本
|
||||
result.push({ type: 'text', text: input });
|
||||
return result;
|
||||
}
|
||||
// Load image
|
||||
const parseUserContent = async (content: string | ChatCompletionContentPart[]) => {
|
||||
@@ -235,11 +211,40 @@ export const loadRequestMessages = async ({
|
||||
};
|
||||
}
|
||||
}
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
|
||||
if (item.content !== undefined && !item.content) return;
|
||||
if (Array.isArray(item.content) && item.content.length === 0) return;
|
||||
}
|
||||
|
||||
return item;
|
||||
})
|
||||
.filter(Boolean) as ChatCompletionMessageParam[];
|
||||
};
|
||||
/*
|
||||
Merge data for some consecutive roles
|
||||
1. Contiguous assistant and both have content, merge content
|
||||
*/
|
||||
const mergeConsecutiveMessages = (
|
||||
messages: ChatCompletionMessageParam[]
|
||||
): ChatCompletionMessageParam[] => {
|
||||
return messages.reduce((mergedMessages: ChatCompletionMessageParam[], currentMessage) => {
|
||||
const lastMessage = mergedMessages[mergedMessages.length - 1];
|
||||
|
||||
if (
|
||||
lastMessage &&
|
||||
currentMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
|
||||
lastMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
|
||||
typeof lastMessage.content === 'string' &&
|
||||
typeof currentMessage.content === 'string'
|
||||
) {
|
||||
lastMessage.content += currentMessage ? `\n${currentMessage.content}` : '';
|
||||
} else {
|
||||
mergedMessages.push(currentMessage);
|
||||
}
|
||||
|
||||
return mergedMessages;
|
||||
}, []);
|
||||
};
|
||||
|
||||
if (messages.length === 0) {
|
||||
return Promise.reject('core.chat.error.Messages empty');
|
||||
@@ -269,11 +274,22 @@ export const loadRequestMessages = async ({
|
||||
...item,
|
||||
content: await parseUserContent(item.content)
|
||||
};
|
||||
} else if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
|
||||
return {
|
||||
role: item.role,
|
||||
content: item.content,
|
||||
function_call: item.function_call,
|
||||
name: item.name,
|
||||
refusal: item.refusal,
|
||||
tool_calls: item.tool_calls
|
||||
};
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
})
|
||||
)) as ChatCompletionMessageParam[];
|
||||
|
||||
return clearInvalidMessages(loadMessages) as SdkChatCompletionMessageParam[];
|
||||
return mergeConsecutiveMessages(
|
||||
clearInvalidMessages(loadMessages)
|
||||
) as SdkChatCompletionMessageParam[];
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ClientSession } from '../../../common/mongo';
|
||||
import { getLLMModel, getVectorModel } from '../../ai/model';
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import { getCollectionWithDataset } from '../controller';
|
||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
|
||||
export const lockTrainingDataByTeamId = async (teamId: string): Promise<any> => {
|
||||
try {
|
||||
@@ -64,7 +65,7 @@ export async function pushDataListToTrainingQueue({
|
||||
vectorModel: string;
|
||||
session?: ClientSession;
|
||||
} & PushDatasetDataProps): Promise<PushDatasetDataResponse> {
|
||||
const checkModelValid = async () => {
|
||||
const { model, maxToken, weight } = await (async () => {
|
||||
const agentModelData = getLLMModel(agentModel);
|
||||
if (!agentModelData) {
|
||||
return Promise.reject(`File model ${agentModel} is inValid`);
|
||||
@@ -91,9 +92,16 @@ export async function pushDataListToTrainingQueue({
|
||||
}
|
||||
|
||||
return Promise.reject(`Training mode "${trainingMode}" is inValid`);
|
||||
};
|
||||
})();
|
||||
|
||||
const { model, maxToken, weight } = await checkModelValid();
|
||||
// filter repeat or equal content
|
||||
const set = new Set();
|
||||
const filterResult: Record<string, PushDatasetDataChunkProps[]> = {
|
||||
success: [],
|
||||
overToken: [],
|
||||
repeat: [],
|
||||
error: []
|
||||
};
|
||||
|
||||
// format q and a, remove empty char
|
||||
data.forEach((item) => {
|
||||
@@ -108,19 +116,8 @@ export async function pushDataListToTrainingQueue({
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
});
|
||||
|
||||
// filter repeat or equal content
|
||||
const set = new Set();
|
||||
const filterResult: Record<string, PushDatasetDataChunkProps[]> = {
|
||||
success: [],
|
||||
overToken: [],
|
||||
repeat: [],
|
||||
error: []
|
||||
};
|
||||
|
||||
// filter repeat content
|
||||
data.forEach((item) => {
|
||||
// filter repeat content
|
||||
if (!item.q) {
|
||||
filterResult.error.push(item);
|
||||
return;
|
||||
@@ -150,40 +147,55 @@ export async function pushDataListToTrainingQueue({
|
||||
const failedDocuments: PushDatasetDataChunkProps[] = [];
|
||||
|
||||
// 使用 insertMany 批量插入
|
||||
try {
|
||||
await MongoDatasetTraining.insertMany(
|
||||
filterResult.success.map((item) => ({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
billId,
|
||||
mode: trainingMode,
|
||||
prompt,
|
||||
model,
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
chunkIndex: item.chunkIndex ?? 0,
|
||||
weight: weight ?? 0,
|
||||
indexes: item.indexes
|
||||
})),
|
||||
{
|
||||
session,
|
||||
ordered: false
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
addLog.error(`Insert error`, error);
|
||||
// 如果有错误,将失败的文档添加到失败列表中
|
||||
error.writeErrors?.forEach((writeError: any) => {
|
||||
failedDocuments.push(data[writeError.index]);
|
||||
});
|
||||
console.log('failed', failedDocuments);
|
||||
}
|
||||
const batchSize = 200;
|
||||
const insertData = async (startIndex: number, session: ClientSession) => {
|
||||
const list = filterResult.success.slice(startIndex, startIndex + batchSize);
|
||||
|
||||
// 对于失败的文档,尝试单独插入
|
||||
for await (const item of failedDocuments) {
|
||||
await MongoDatasetTraining.create(item);
|
||||
if (list.length === 0) return;
|
||||
|
||||
try {
|
||||
await MongoDatasetTraining.insertMany(
|
||||
list.map((item) => ({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
billId,
|
||||
mode: trainingMode,
|
||||
prompt,
|
||||
model,
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
chunkIndex: item.chunkIndex ?? 0,
|
||||
weight: weight ?? 0,
|
||||
indexes: item.indexes
|
||||
})),
|
||||
{
|
||||
session,
|
||||
ordered: true
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
addLog.error(`Insert error`, error);
|
||||
// 如果有错误,将失败的文档添加到失败列表中
|
||||
error.writeErrors?.forEach((writeError: any) => {
|
||||
failedDocuments.push(data[writeError.index]);
|
||||
});
|
||||
console.log('failed', failedDocuments);
|
||||
}
|
||||
console.log(startIndex, '===');
|
||||
// 对于失败的文档,尝试单独插入
|
||||
await MongoDatasetTraining.create(failedDocuments, { session });
|
||||
|
||||
return insertData(startIndex + batchSize, session);
|
||||
};
|
||||
|
||||
if (session) {
|
||||
await insertData(0, session);
|
||||
} else {
|
||||
await mongoSessionRun(async (session) => {
|
||||
await insertData(0, session);
|
||||
});
|
||||
}
|
||||
|
||||
delete filterResult.success;
|
||||
|
||||
3
packages/service/core/workflow/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const WORKFLOW_MAX_RUN_TIMES = process.env.WORKFLOW_MAX_RUN_TIMES
|
||||
? parseInt(process.env.WORKFLOW_MAX_RUN_TIMES)
|
||||
: 500;
|
||||
@@ -31,7 +31,7 @@ type Response = DispatchNodeResultType<{
|
||||
|
||||
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
app: workflowApp,
|
||||
runningAppInfo,
|
||||
workflowStreamResponse,
|
||||
histories,
|
||||
query,
|
||||
@@ -45,7 +45,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
// 检查该工作流的tmb是否有调用该app的权限(不是校验对话的人,是否有权限)
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId: app.id,
|
||||
tmbId: workflowApp.tmbId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
|
||||
@@ -61,7 +61,11 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||
...props,
|
||||
app: appData,
|
||||
runningAppInfo: {
|
||||
id: String(appData._id),
|
||||
teamId: String(appData.teamId),
|
||||
tmbId: String(appData.tmbId)
|
||||
},
|
||||
runtimeNodes: storeNodes2RuntimeNodes(
|
||||
appData.modules,
|
||||
getWorkflowEntryNodeIds(appData.modules)
|
||||
|
||||
@@ -194,7 +194,10 @@ export const runToolWithFunctionCall = async (
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: item
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
@@ -295,7 +298,10 @@ export const runToolWithFunctionCall = async (
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages: filterMessages,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -307,7 +313,10 @@ export const runToolWithFunctionCall = async (
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -327,7 +336,8 @@ export const runToolWithFunctionCall = async (
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -125,7 +125,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
dispatchFlowResponse, // tool flow response
|
||||
totalTokens,
|
||||
completeMessages = [], // The actual message sent to AI(just save text)
|
||||
assistantResponses = [] // FastGPT system store assistant.value response
|
||||
assistantResponses = [], // FastGPT system store assistant.value response
|
||||
runTimes
|
||||
} = await (async () => {
|
||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
||||
|
||||
@@ -134,6 +135,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
...props,
|
||||
toolNodes,
|
||||
toolModel,
|
||||
maxRunToolTimes: 30,
|
||||
messages: adaptMessages
|
||||
});
|
||||
}
|
||||
@@ -194,6 +196,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
const previewAssistantResponses = filterToolResponseToPreview(assistantResponses);
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
|
||||
[NodeOutputKeyEnum.answerText]: previewAssistantResponses
|
||||
.filter((item) => item.text?.content)
|
||||
.map((item) => item.text?.content || '')
|
||||
|
||||
@@ -180,7 +180,8 @@ export const runToolWithPromptCall = async (
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
};
|
||||
}
|
||||
|
||||
@@ -226,7 +227,10 @@ export const runToolWithPromptCall = async (
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: item
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
@@ -315,7 +319,8 @@ ANSWER: `;
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages: filterMessages,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
|
||||
};
|
||||
}
|
||||
|
||||
@@ -327,7 +332,8 @@ ANSWER: `;
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { updateToolInputValue } from './utils';
|
||||
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
|
||||
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||
import { addLog } from '../../../../../common/system/log';
|
||||
|
||||
type ToolRunResponseType = {
|
||||
@@ -45,13 +45,12 @@ export const runToolWithToolChoice = async (
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
toolModel: LLMModelItemType;
|
||||
maxRunToolTimes: number;
|
||||
},
|
||||
response?: RunToolResponse
|
||||
): Promise<RunToolResponse> => {
|
||||
const { messages, toolNodes, toolModel, maxRunToolTimes, ...workflowProps } = props;
|
||||
const {
|
||||
toolModel,
|
||||
toolNodes,
|
||||
messages,
|
||||
res,
|
||||
requestOrigin,
|
||||
runtimeNodes,
|
||||
@@ -59,7 +58,12 @@ export const runToolWithToolChoice = async (
|
||||
stream,
|
||||
workflowStreamResponse,
|
||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||
} = props;
|
||||
} = workflowProps;
|
||||
|
||||
if (maxRunToolTimes <= 0 && response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const assistantResponses = response?.assistantResponses || [];
|
||||
|
||||
const tools: ChatCompletionTool[] = toolNodes.map((item) => {
|
||||
@@ -196,7 +200,7 @@ export const runToolWithToolChoice = async (
|
||||
})();
|
||||
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
...workflowProps,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
@@ -205,7 +209,10 @@ export const runToolWithToolChoice = async (
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: item
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
@@ -252,11 +259,22 @@ export const runToolWithToolChoice = async (
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
tool_calls: toolCalls
|
||||
};
|
||||
/*
|
||||
...
|
||||
user
|
||||
assistant: tool data
|
||||
*/
|
||||
const concatToolMessages = [
|
||||
...requestMessages,
|
||||
assistantToolMsgParams
|
||||
] as ChatCompletionMessageParam[];
|
||||
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
|
||||
/*
|
||||
...
|
||||
user
|
||||
assistant: tool data
|
||||
tool: tool response
|
||||
*/
|
||||
const completeMessages = [
|
||||
...concatToolMessages,
|
||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||
@@ -307,19 +325,26 @@ export const runToolWithToolChoice = async (
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
};
|
||||
}
|
||||
|
||||
return runToolWithToolChoice(
|
||||
{
|
||||
...props,
|
||||
maxRunToolTimes: maxRunToolTimes - 1,
|
||||
messages: completeMessages
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
assistantResponses: toolNodeAssistants
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -339,10 +364,12 @@ export const runToolWithToolChoice = async (
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
addLog.warn(`LLM response error`, {
|
||||
requestBody
|
||||
});
|
||||
@@ -367,6 +394,7 @@ async function streamResponse({
|
||||
});
|
||||
|
||||
let textAnswer = '';
|
||||
let callingTool: { name: string; arguments: string } | null = null;
|
||||
let toolCalls: ChatCompletionMessageToolCall[] = [];
|
||||
|
||||
for await (const part of stream) {
|
||||
@@ -390,69 +418,71 @@ async function streamResponse({
|
||||
});
|
||||
} else if (responseChoice?.tool_calls?.[0]) {
|
||||
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
|
||||
|
||||
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool
|
||||
if (toolCall.id) {
|
||||
const toolNode = toolNodes.find((item) => item.nodeId === toolCall.function?.name);
|
||||
if (toolCall.id || callingTool) {
|
||||
// Start call tool
|
||||
if (toolCall.id) {
|
||||
callingTool = {
|
||||
name: toolCall.function.name || '',
|
||||
arguments: toolCall.function.arguments || ''
|
||||
};
|
||||
} else if (callingTool) {
|
||||
// Continue call
|
||||
callingTool.name += toolCall.function.name || '';
|
||||
callingTool.arguments += toolCall.function.arguments || '';
|
||||
}
|
||||
|
||||
const toolFunction = callingTool!;
|
||||
|
||||
const toolNode = toolNodes.find((item) => item.nodeId === toolFunction.name);
|
||||
|
||||
if (toolNode) {
|
||||
if (toolCall.function?.arguments === undefined) {
|
||||
toolCall.function.arguments = '';
|
||||
}
|
||||
// New tool, add to list.
|
||||
const toolId = getNanoid();
|
||||
toolCalls.push({
|
||||
...toolCall,
|
||||
id: toolId,
|
||||
function: toolFunction,
|
||||
toolName: toolNode.name,
|
||||
toolAvatar: toolNode.avatar
|
||||
});
|
||||
|
||||
// Get last tool call
|
||||
const lastToolCall = toolCalls[toolCalls.length - 1];
|
||||
|
||||
// new tool
|
||||
if (lastToolCall?.id !== toolCall.id) {
|
||||
toolCalls.push({
|
||||
...toolCall,
|
||||
toolName: toolNode.name,
|
||||
toolAvatar: toolNode.avatar
|
||||
});
|
||||
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolCall,
|
||||
data: {
|
||||
tool: {
|
||||
id: toolCall.id,
|
||||
toolName: toolNode.name,
|
||||
toolAvatar: toolNode.avatar,
|
||||
functionName: toolCall.function.name,
|
||||
params: toolCall.function.arguments,
|
||||
response: ''
|
||||
}
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolCall,
|
||||
data: {
|
||||
tool: {
|
||||
id: toolId,
|
||||
toolName: toolNode.name,
|
||||
toolAvatar: toolNode.avatar,
|
||||
functionName: toolFunction.name,
|
||||
params: toolFunction?.arguments ?? '',
|
||||
response: ''
|
||||
}
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
// last tool, update params
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* arg 插入最后一个工具的参数里 */
|
||||
const arg: string = toolCall?.function?.arguments ?? '';
|
||||
const currentTool = toolCalls[toolCalls.length - 1];
|
||||
|
||||
if (currentTool) {
|
||||
currentTool.function.arguments += arg;
|
||||
|
||||
workflowStreamResponse?.({
|
||||
write,
|
||||
event: SseResponseEventEnum.toolParams,
|
||||
data: {
|
||||
tool: {
|
||||
id: currentTool.id,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: arg,
|
||||
response: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
callingTool = null;
|
||||
}
|
||||
} else {
|
||||
/* arg 插入最后一个工具的参数里 */
|
||||
const arg: string = toolCall?.function?.arguments ?? '';
|
||||
const currentTool = toolCalls[toolCalls.length - 1];
|
||||
if (currentTool && arg) {
|
||||
currentTool.function.arguments += arg;
|
||||
|
||||
workflowStreamResponse?.({
|
||||
write,
|
||||
event: SseResponseEventEnum.toolParams,
|
||||
data: {
|
||||
tool: {
|
||||
id: currentTool.id,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: arg,
|
||||
response: ''
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/
|
||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import type { DispatchFlowResponse } from '../../type.d';
|
||||
import { AIChatItemValueItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
|
||||
export type DispatchToolModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -25,6 +26,7 @@ export type RunToolResponse = {
|
||||
totalTokens: number;
|
||||
completeMessages?: ChatCompletionMessageParam[];
|
||||
assistantResponses?: AIChatItemValueItemType[];
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: number;
|
||||
};
|
||||
export type ToolNodeItemType = RuntimeNodeItemType & {
|
||||
toolParams: RuntimeNodeItemType['inputs'];
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function dispatchDatasetSearch(
|
||||
props: DatasetSearchProps
|
||||
): Promise<DatasetSearchResponse> {
|
||||
const {
|
||||
teamId,
|
||||
runningAppInfo: { teamId },
|
||||
histories,
|
||||
node,
|
||||
params: {
|
||||
|
||||
@@ -6,7 +6,9 @@ import {
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type {
|
||||
ChatDispatchProps,
|
||||
ModuleDispatchProps
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps,
|
||||
SystemVariablesType
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
|
||||
import type {
|
||||
@@ -63,7 +65,7 @@ import {
|
||||
InteractiveNodeResponseItemType,
|
||||
UserSelectInteractive
|
||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||
import { dispatchRunAppNode } from './agent/runApp';
|
||||
import { dispatchRunAppNode } from './plugin/runApp';
|
||||
|
||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||
@@ -117,6 +119,31 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
...props
|
||||
} = data;
|
||||
|
||||
// 初始化深度和自动增加深度,避免无限嵌套
|
||||
if (!props.workflowDispatchDeep) {
|
||||
props.workflowDispatchDeep = 1;
|
||||
} else {
|
||||
props.workflowDispatchDeep += 1;
|
||||
}
|
||||
|
||||
if (props.workflowDispatchDeep > 20) {
|
||||
return {
|
||||
flowResponses: [],
|
||||
flowUsages: [],
|
||||
debugResponse: {
|
||||
finishedNodes: [],
|
||||
finishedEdges: [],
|
||||
nextStepRunNodes: []
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: 1,
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: [],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: null,
|
||||
newVariables: removeSystemVariable(variables)
|
||||
};
|
||||
}
|
||||
|
||||
let workflowRunTimes = 0;
|
||||
|
||||
// set sse response headers
|
||||
if (stream && res) {
|
||||
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
|
||||
@@ -135,6 +162,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
let chatNodeUsages: ChatNodeUsageType[] = [];
|
||||
let toolRunResponse: ToolRunResponseItemType;
|
||||
let debugNextStepRunNodes: RuntimeNodeItemType[] = [];
|
||||
// 记录交互节点,交互节点需要在工作流完全结束后再进行计算
|
||||
let workflowInteractiveResponse:
|
||||
| {
|
||||
entryNodeIds: string[];
|
||||
interactiveResponse: UserSelectInteractive;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
/* Store special response field */
|
||||
function pushStore(
|
||||
@@ -144,15 +178,21 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
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
|
||||
}
|
||||
assistantResponses,
|
||||
rewriteHistories,
|
||||
runTimes = 1
|
||||
}: Omit<
|
||||
DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.answerText]?: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
|
||||
}>,
|
||||
'nodeResponse'
|
||||
>
|
||||
) {
|
||||
// Add run times
|
||||
workflowRunTimes += runTimes;
|
||||
props.maxRunTimes -= runTimes;
|
||||
|
||||
if (responseData) {
|
||||
chatResponses.push(responseData);
|
||||
}
|
||||
@@ -181,12 +221,19 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (rewriteHistories) {
|
||||
histories = rewriteHistories;
|
||||
}
|
||||
}
|
||||
/* Pass the output of the node, to get next nodes and update edge status */
|
||||
function nodeOutput(
|
||||
node: RuntimeNodeItemType,
|
||||
result: Record<string, any> = {}
|
||||
): RuntimeNodeItemType[] {
|
||||
): {
|
||||
nextStepActiveNodes: RuntimeNodeItemType[];
|
||||
nextStepSkipNodes: RuntimeNodeItemType[];
|
||||
} {
|
||||
pushStore(node, result);
|
||||
|
||||
// Assign the output value to the next node
|
||||
@@ -211,16 +258,32 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
}
|
||||
});
|
||||
|
||||
const nextStepNodes = runtimeNodes.filter((node) => {
|
||||
return targetEdges.some((item) => item.target === node.nodeId);
|
||||
const nextStepActiveNodes: RuntimeNodeItemType[] = [];
|
||||
const nextStepSkipNodes: RuntimeNodeItemType[] = [];
|
||||
runtimeNodes.forEach((node) => {
|
||||
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'active')) {
|
||||
nextStepActiveNodes.push(node);
|
||||
}
|
||||
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'skipped')) {
|
||||
nextStepSkipNodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
if (props.mode === 'debug') {
|
||||
debugNextStepRunNodes = debugNextStepRunNodes.concat(nextStepNodes);
|
||||
return [];
|
||||
debugNextStepRunNodes = debugNextStepRunNodes.concat([
|
||||
...nextStepActiveNodes,
|
||||
...nextStepSkipNodes
|
||||
]);
|
||||
return {
|
||||
nextStepActiveNodes: [],
|
||||
nextStepSkipNodes: []
|
||||
};
|
||||
}
|
||||
|
||||
return nextStepNodes;
|
||||
return {
|
||||
nextStepActiveNodes,
|
||||
nextStepSkipNodes
|
||||
};
|
||||
}
|
||||
|
||||
/* Have interactive result, computed edges and node outputs */
|
||||
@@ -270,10 +333,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
};
|
||||
}
|
||||
|
||||
// 每个节点 运行/跳过 后,初始化边的状态
|
||||
function nodeRunAfterHook(node: RuntimeNodeItemType) {
|
||||
node.isEntry = false;
|
||||
|
||||
// 每个节点确定 运行/跳过 前,初始化边的状态
|
||||
function nodeRunBeforeHook(node: RuntimeNodeItemType) {
|
||||
runtimeEdges.forEach((item) => {
|
||||
if (item.target === node.nodeId) {
|
||||
item.status = 'waiting';
|
||||
@@ -281,69 +342,81 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
});
|
||||
}
|
||||
/* Check node run/skip or wait */
|
||||
function checkNodeCanRun(nodes: RuntimeNodeItemType[] = []): Promise<any> {
|
||||
return Promise.all(
|
||||
nodes.map(async (node) => {
|
||||
const status = checkNodeRunStatus({
|
||||
node,
|
||||
runtimeEdges
|
||||
});
|
||||
async function checkNodeCanRun(
|
||||
node: RuntimeNodeItemType,
|
||||
skippedNodeIdList = new Set<string>()
|
||||
): Promise<RuntimeNodeItemType[]> {
|
||||
if (res?.closed || props.maxRunTimes <= 0) return [];
|
||||
// Thread avoidance
|
||||
await surrenderProcess();
|
||||
|
||||
if (res?.closed || props.maxRunTimes <= 0) return;
|
||||
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, appId: props.runningAppInfo.id });
|
||||
|
||||
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
|
||||
|
||||
// Thread avoidance
|
||||
await surrenderProcess();
|
||||
|
||||
if (status === 'run') {
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||
return nodeRunWithActive(node);
|
||||
}
|
||||
if (status === 'skip') {
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||
return nodeRunWithSkip(node);
|
||||
}
|
||||
|
||||
return;
|
||||
})
|
||||
).then((result) => {
|
||||
props.maxRunTimes--;
|
||||
|
||||
const flat = result.flat().filter(Boolean) as unknown as {
|
||||
node: RuntimeNodeItemType;
|
||||
runStatus: 'run' | 'skip';
|
||||
result: Record<string, any>;
|
||||
}[];
|
||||
// If there are no running nodes, the workflow is complete
|
||||
if (flat.length === 0) return;
|
||||
|
||||
// Update the node output at the end of the run and get the next nodes
|
||||
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
|
||||
// Remove repeat nodes(Make sure that the node is only executed once)
|
||||
const filterNextNodes = nextNodes.filter(
|
||||
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||
);
|
||||
|
||||
// In the current version, only one interactive node is allowed at the same time
|
||||
const haveInteractiveResponse = flat
|
||||
.map((response) => {
|
||||
const interactiveResponse = response.result?.[DispatchNodeResponseKeyEnum.interactive];
|
||||
if (interactiveResponse) {
|
||||
chatAssistantResponse.push(
|
||||
handleInteractiveResult({
|
||||
entryNodeIds: [response.node.nodeId],
|
||||
interactiveResponse
|
||||
})
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
if (haveInteractiveResponse.length > 0) return;
|
||||
|
||||
return checkNodeCanRun(filterNextNodes);
|
||||
// Get node run status by edges
|
||||
const status = checkNodeRunStatus({
|
||||
node,
|
||||
runtimeEdges
|
||||
});
|
||||
const nodeRunResult = await (() => {
|
||||
if (status === 'run') {
|
||||
nodeRunBeforeHook(node);
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||
return nodeRunWithActive(node);
|
||||
}
|
||||
if (status === 'skip' && !skippedNodeIdList.has(node.nodeId)) {
|
||||
nodeRunBeforeHook(node);
|
||||
props.maxRunTimes -= 0.1;
|
||||
skippedNodeIdList.add(node.nodeId);
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||
return nodeRunWithSkip(node);
|
||||
}
|
||||
})();
|
||||
|
||||
if (!nodeRunResult) return [];
|
||||
|
||||
// In the current version, only one interactive node is allowed at the same time
|
||||
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
|
||||
if (interactiveResponse) {
|
||||
workflowInteractiveResponse = {
|
||||
entryNodeIds: [nodeRunResult.node.nodeId],
|
||||
interactiveResponse
|
||||
};
|
||||
return [];
|
||||
}
|
||||
|
||||
// Update the node output at the end of the run and get the next nodes
|
||||
let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput(
|
||||
nodeRunResult.node,
|
||||
nodeRunResult.result
|
||||
);
|
||||
// Remove repeat nodes(Make sure that the node is only executed once)
|
||||
nextStepActiveNodes = nextStepActiveNodes.filter(
|
||||
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||
);
|
||||
nextStepSkipNodes = nextStepSkipNodes.filter(
|
||||
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||
);
|
||||
|
||||
// Run next nodes(先运行 run 的,再运行 skip 的)
|
||||
const nextStepActiveNodesResults = (
|
||||
await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node)))
|
||||
).flat();
|
||||
|
||||
// 如果已经 active 运行过,不再执行 skip(active 中有闭环)
|
||||
nextStepSkipNodes = nextStepSkipNodes.filter(
|
||||
(node) => !nextStepActiveNodesResults.some((item) => item.nodeId === node.nodeId)
|
||||
);
|
||||
|
||||
const nextStepSkipNodesResults = (
|
||||
await Promise.all(nextStepSkipNodes.map((node) => checkNodeCanRun(node, skippedNodeIdList)))
|
||||
).flat();
|
||||
|
||||
return [
|
||||
...nextStepActiveNodes,
|
||||
...nextStepSkipNodes,
|
||||
...nextStepActiveNodesResults,
|
||||
...nextStepSkipNodesResults
|
||||
];
|
||||
}
|
||||
/* Inject data into module input */
|
||||
function getNodeRunParams(node: RuntimeNodeItemType) {
|
||||
@@ -396,7 +469,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
|
||||
return params;
|
||||
}
|
||||
async function nodeRunWithActive(node: RuntimeNodeItemType) {
|
||||
async function nodeRunWithActive(node: RuntimeNodeItemType): Promise<{
|
||||
node: RuntimeNodeItemType;
|
||||
runStatus: 'run';
|
||||
result: Record<string, any>;
|
||||
}> {
|
||||
// push run status messages
|
||||
if (node.showStatus) {
|
||||
props.workflowStreamResponse?.({
|
||||
@@ -454,8 +531,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
dispatchRes[item.key] = valueTypeFormat(item.defaultValue, item.valueType);
|
||||
});
|
||||
|
||||
nodeRunAfterHook(node);
|
||||
|
||||
return {
|
||||
node,
|
||||
runStatus: 'run',
|
||||
@@ -465,10 +540,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
}
|
||||
};
|
||||
}
|
||||
async function nodeRunWithSkip(node: RuntimeNodeItemType) {
|
||||
// 其后所有target的节点,都设置为skip
|
||||
async function nodeRunWithSkip(node: RuntimeNodeItemType): Promise<{
|
||||
node: RuntimeNodeItemType;
|
||||
runStatus: 'skip';
|
||||
result: Record<string, any>;
|
||||
}> {
|
||||
// Set target edges status to skipped
|
||||
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
||||
nodeRunAfterHook(node);
|
||||
|
||||
return {
|
||||
node,
|
||||
@@ -483,10 +561,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
const entryNodes = runtimeNodes.filter((item) => item.isEntry);
|
||||
|
||||
// reset entry
|
||||
// runtimeNodes.forEach((item) => {
|
||||
// item.isEntry = false;
|
||||
// });
|
||||
await checkNodeCanRun(entryNodes);
|
||||
runtimeNodes.forEach((item) => {
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (item.flowNodeType !== FlowNodeTypeEnum.userSelect) {
|
||||
item.isEntry = false;
|
||||
}
|
||||
});
|
||||
await Promise.all(entryNodes.map((node) => checkNodeCanRun(node)));
|
||||
|
||||
// focus try to run pluginOutput
|
||||
const pluginOutputModule = runtimeNodes.find(
|
||||
@@ -496,6 +577,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
await nodeRunWithActive(pluginOutputModule);
|
||||
}
|
||||
|
||||
// Interactive node
|
||||
if (workflowInteractiveResponse) {
|
||||
const interactiveResult = handleInteractiveResult({
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
interactiveResponse: workflowInteractiveResponse.interactiveResponse
|
||||
});
|
||||
chatAssistantResponse.push(interactiveResult);
|
||||
}
|
||||
|
||||
return {
|
||||
flowResponses: chatResponses,
|
||||
flowUsages: chatNodeUsages,
|
||||
@@ -504,6 +594,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
finishedEdges: runtimeEdges,
|
||||
nextStepRunNodes: debugNextStepRunNodes
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: workflowRunTimes,
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
||||
mergeAssistantResponseAnswerText(chatAssistantResponse),
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
|
||||
@@ -514,13 +605,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
/* get system variable */
|
||||
export function getSystemVariable({
|
||||
user,
|
||||
app,
|
||||
runningAppInfo,
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories = []
|
||||
}: Props) {
|
||||
histories = [],
|
||||
uid
|
||||
}: Props): SystemVariablesType {
|
||||
return {
|
||||
appId: String(app._id),
|
||||
userId: uid,
|
||||
appId: String(runningAppInfo.id),
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
DispatchNodeResponseKeyEnum,
|
||||
SseResponseEventEnum
|
||||
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import {
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps
|
||||
@@ -12,8 +9,6 @@ import type {
|
||||
UserSelectInteractive,
|
||||
UserSelectOptionItemType
|
||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||
import { updateUserSelectedResult } from '../../../chat/controller';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
@@ -28,37 +23,28 @@ type UserSelectResponse = DispatchNodeResultType<{
|
||||
|
||||
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
|
||||
const {
|
||||
workflowStreamResponse,
|
||||
app: { _id: appId },
|
||||
chatId,
|
||||
node: { nodeId, isEntry },
|
||||
histories,
|
||||
node,
|
||||
params: { description, userSelectOptions },
|
||||
query
|
||||
} = props;
|
||||
const { nodeId, isEntry } = node;
|
||||
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (!isEntry) {
|
||||
const answerText = description ? `\n${description}` : undefined;
|
||||
if (answerText) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.fastAnswer,
|
||||
data: textAdaptGptResponse({
|
||||
text: answerText
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
[NodeOutputKeyEnum.answerText]: answerText,
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'userSelect',
|
||||
params: {
|
||||
description,
|
||||
userSelectOptions
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
node.isEntry = false;
|
||||
|
||||
const { text: userSelectedVal } = chatValue2RuntimePrompt(query);
|
||||
|
||||
// Error status
|
||||
@@ -70,14 +56,8 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
};
|
||||
}
|
||||
|
||||
// Update db
|
||||
updateUserSelectedResult({
|
||||
appId,
|
||||
chatId,
|
||||
userSelectedVal
|
||||
});
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), // Removes the current session record as the history of subsequent nodes
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]: userSelectOptions
|
||||
.filter((item) => item.value !== userSelectedVal)
|
||||
.map((item: any) => getHandleId(nodeId, 'source', item.key)),
|
||||
|
||||
@@ -13,6 +13,7 @@ import { authPluginByTmbId } from '../../../../support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { computedPluginUsage } from '../../../app/plugin/utils';
|
||||
import { filterSystemVariables } from '../utils';
|
||||
import { getPluginRunUserQuery } from '../../utils';
|
||||
|
||||
type RunPluginProps = ModuleDispatchProps<{
|
||||
[key: string]: any;
|
||||
@@ -22,9 +23,8 @@ type RunPluginResponse = DispatchNodeResultType<{}>;
|
||||
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
|
||||
const {
|
||||
node: { pluginId },
|
||||
app: workflowApp,
|
||||
runningAppInfo,
|
||||
mode,
|
||||
teamId,
|
||||
params: data // Plugin input
|
||||
} = props;
|
||||
|
||||
@@ -33,9 +33,9 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
}
|
||||
|
||||
// auth plugin
|
||||
await authPluginByTmbId({
|
||||
const pluginData = await authPluginByTmbId({
|
||||
appId: pluginId,
|
||||
tmbId: workflowApp.tmbId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
|
||||
@@ -61,10 +61,21 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
showStatus: false
|
||||
};
|
||||
});
|
||||
const runtimeVariables = {
|
||||
...filterSystemVariables(props.variables),
|
||||
appId: String(plugin.id)
|
||||
};
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||
const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
|
||||
...props,
|
||||
variables: filterSystemVariables(props.variables),
|
||||
runningAppInfo: {
|
||||
id: String(plugin.id),
|
||||
teamId: plugin.teamId || '',
|
||||
tmbId: pluginData?.tmbId || ''
|
||||
},
|
||||
variables: runtimeVariables,
|
||||
query: getPluginRunUserQuery(plugin.nodes, runtimeVariables).value,
|
||||
chatConfig: {},
|
||||
runtimeNodes,
|
||||
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
|
||||
});
|
||||
@@ -81,12 +92,13 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
return {
|
||||
assistantResponses,
|
||||
// responseData, // debug
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
moduleLogo: plugin.avatar,
|
||||
totalPoints: usagePoints,
|
||||
pluginOutput: output?.pluginOutput,
|
||||
pluginDetail:
|
||||
mode === 'test' && plugin.teamId === teamId
|
||||
mode === 'test' && plugin.teamId === runningAppInfo.teamId
|
||||
? flowResponses.filter((item) => {
|
||||
const filterArr = [FlowNodeTypeEnum.pluginOutput];
|
||||
return !filterArr.includes(item.moduleType as any);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/glob
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { authAppByTmbId } from '../../../../support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { getAppLatestVersion } from '../../../app/controller';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
@@ -29,7 +30,7 @@ type Response = DispatchNodeResultType<{
|
||||
|
||||
export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
app: workflowApp,
|
||||
runningAppInfo,
|
||||
histories,
|
||||
query,
|
||||
node: { pluginId },
|
||||
@@ -49,9 +50,10 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
||||
// Auth the app by tmbId(Not the user, but the workflow user)
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId: pluginId,
|
||||
tmbId: workflowApp.tmbId,
|
||||
tmbId: runningAppInfo.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
const { nodes, edges, chatConfig } = await getAppLatestVersion(pluginId);
|
||||
|
||||
// Auto line
|
||||
workflowStreamResponse?.({
|
||||
@@ -64,27 +66,31 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
||||
const chatHistories = getHistories(history, histories);
|
||||
const { files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
// Concat variables
|
||||
// Rewrite children app variables
|
||||
const systemVariables = filterSystemVariables(variables);
|
||||
const childrenRunVariables = {
|
||||
...systemVariables,
|
||||
...childrenAppVariables
|
||||
...childrenAppVariables,
|
||||
histories: chatHistories,
|
||||
appId: String(appData._id)
|
||||
};
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||
const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
|
||||
...props,
|
||||
app: appData,
|
||||
runtimeNodes: storeNodes2RuntimeNodes(
|
||||
appData.modules,
|
||||
getWorkflowEntryNodeIds(appData.modules)
|
||||
),
|
||||
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||
runningAppInfo: {
|
||||
id: String(appData._id),
|
||||
teamId: String(appData.teamId),
|
||||
tmbId: String(appData.tmbId)
|
||||
},
|
||||
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(edges),
|
||||
histories: chatHistories,
|
||||
variables: childrenRunVariables,
|
||||
query: runtimePrompt2ChatsValue({
|
||||
files,
|
||||
text: userChatInput
|
||||
}),
|
||||
variables: childrenRunVariables
|
||||
chatConfig
|
||||
});
|
||||
|
||||
const completeMessages = chatHistories.concat([
|
||||
@@ -101,6 +107,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
|
||||
const { text } = chatValue2RuntimePrompt(assistantResponses);
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
moduleLogo: appData.avatar,
|
||||
query: userChatInput,
|
||||
@@ -15,7 +15,7 @@ type Response = DispatchNodeResultType<{}>;
|
||||
|
||||
export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
|
||||
const {
|
||||
app: { _id: appId },
|
||||
runningAppInfo: { id: appId },
|
||||
chatId,
|
||||
responseChatItemId: chatItemId,
|
||||
stream,
|
||||
|
||||
@@ -16,6 +16,8 @@ import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/ty
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { getSystemPluginCb } from '../../../../../plugins/register';
|
||||
import { ContentTypes } from '@fastgpt/global/core/workflow/constants';
|
||||
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
type PropsArrType = {
|
||||
key: string;
|
||||
@@ -29,6 +31,8 @@ type HttpRequestProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.httpHeaders]: PropsArrType[];
|
||||
[NodeInputKeyEnum.httpParams]: PropsArrType[];
|
||||
[NodeInputKeyEnum.httpJsonBody]: string;
|
||||
[NodeInputKeyEnum.httpFormBody]: PropsArrType[];
|
||||
[NodeInputKeyEnum.httpContentType]: ContentTypes;
|
||||
[NodeInputKeyEnum.addInputParam]: Record<string, any>;
|
||||
[NodeInputKeyEnum.httpTimeout]?: number;
|
||||
[key: string]: any;
|
||||
@@ -40,13 +44,23 @@ type HttpResponse = DispatchNodeResultType<{
|
||||
|
||||
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
||||
|
||||
const contentTypeMap = {
|
||||
[ContentTypes.none]: '',
|
||||
[ContentTypes.formData]: '',
|
||||
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
|
||||
[ContentTypes.json]: 'application/json',
|
||||
[ContentTypes.xml]: 'application/xml',
|
||||
[ContentTypes.raw]: 'text/plain'
|
||||
};
|
||||
|
||||
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
||||
let {
|
||||
app: { _id: appId },
|
||||
runningAppInfo: { id: appId },
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
variables,
|
||||
node: { outputs },
|
||||
node,
|
||||
runtimeNodes,
|
||||
histories,
|
||||
workflowStreamResponse,
|
||||
params: {
|
||||
@@ -55,6 +69,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
system_httpHeader: httpHeader,
|
||||
system_httpParams: httpParams = [],
|
||||
system_httpJsonBody: httpJsonBody,
|
||||
system_httpFormBody: httpFormBody,
|
||||
system_httpContentType: httpContentType = ContentTypes.json,
|
||||
system_httpTimeout: httpTimeout = 60,
|
||||
[NodeInputKeyEnum.addInputParam]: dynamicInput,
|
||||
...body
|
||||
@@ -77,21 +93,37 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
// ...dynamicInput,
|
||||
...systemVariables
|
||||
};
|
||||
|
||||
const allVariables = {
|
||||
[NodeInputKeyEnum.addInputParam]: concatVariables,
|
||||
...concatVariables
|
||||
};
|
||||
|
||||
httpReqUrl = replaceVariable(httpReqUrl, allVariables);
|
||||
|
||||
const replaceStringVariables = (text: string) => {
|
||||
return replaceVariable(
|
||||
replaceEditorVariable({
|
||||
text,
|
||||
nodes: runtimeNodes,
|
||||
variables: allVariables,
|
||||
runningNode: node
|
||||
}),
|
||||
allVariables
|
||||
);
|
||||
};
|
||||
|
||||
// parse header
|
||||
const headers = await (() => {
|
||||
try {
|
||||
const contentType = contentTypeMap[httpContentType];
|
||||
if (contentType) {
|
||||
httpHeader = [{ key: 'Content-Type', value: contentType, type: 'string' }, ...httpHeader];
|
||||
}
|
||||
|
||||
if (!httpHeader || httpHeader.length === 0) return {};
|
||||
// array
|
||||
return httpHeader.reduce((acc: Record<string, string>, item) => {
|
||||
const key = replaceVariable(item.key, allVariables);
|
||||
const value = replaceVariable(item.value, allVariables);
|
||||
const key = replaceStringVariables(item.key);
|
||||
const value = replaceStringVariables(item.value);
|
||||
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -99,28 +131,53 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
return Promise.reject('Header 为非法 JSON 格式');
|
||||
}
|
||||
})();
|
||||
|
||||
const params = httpParams.reduce((acc: Record<string, string>, item) => {
|
||||
const key = replaceVariable(item.key, allVariables);
|
||||
const value = replaceVariable(item.value, allVariables);
|
||||
const key = replaceStringVariables(item.key);
|
||||
const value = replaceStringVariables(item.value);
|
||||
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const requestBody = await (() => {
|
||||
if (!httpJsonBody) return {};
|
||||
if (httpContentType === ContentTypes.none) return {};
|
||||
try {
|
||||
// Replace all variables in the string body
|
||||
httpJsonBody = replaceVariable(httpJsonBody, allVariables);
|
||||
|
||||
// Text body, return directly
|
||||
if (headers['Content-Type']?.includes('text/plain')) {
|
||||
return httpJsonBody?.replaceAll(UNDEFINED_SIGN, 'null');
|
||||
if (httpContentType === ContentTypes.formData) {
|
||||
if (!Array.isArray(httpFormBody)) return {};
|
||||
httpFormBody = httpFormBody.map((item) => ({
|
||||
key: replaceStringVariables(item.key),
|
||||
type: item.type,
|
||||
value: replaceStringVariables(item.value)
|
||||
}));
|
||||
const formData = new FormData();
|
||||
for (const { key, value } of httpFormBody) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
|
||||
// Json body, parse and return
|
||||
const jsonParse = JSON.parse(httpJsonBody);
|
||||
const removeSignJson = removeUndefinedSign(jsonParse);
|
||||
return removeSignJson;
|
||||
if (httpContentType === ContentTypes.xWwwFormUrlencoded) {
|
||||
if (!Array.isArray(httpFormBody)) return {};
|
||||
httpFormBody = httpFormBody.map((item) => ({
|
||||
key: replaceStringVariables(item.key),
|
||||
type: item.type,
|
||||
value: replaceStringVariables(item.value)
|
||||
}));
|
||||
const urlSearchParams = new URLSearchParams();
|
||||
for (const { key, value } of httpFormBody) {
|
||||
urlSearchParams.append(key, value);
|
||||
}
|
||||
return urlSearchParams;
|
||||
}
|
||||
if (!httpJsonBody) return {};
|
||||
if (httpContentType === ContentTypes.json) {
|
||||
httpJsonBody = replaceVariable(httpJsonBody, allVariables);
|
||||
// Json body, parse and return
|
||||
const jsonParse = JSON.parse(httpJsonBody);
|
||||
const removeSignJson = removeUndefinedSign(jsonParse);
|
||||
return removeSignJson;
|
||||
}
|
||||
httpJsonBody = replaceStringVariables(httpJsonBody);
|
||||
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
|
||||
@@ -150,7 +207,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
// format output value type
|
||||
const results: Record<string, any> = {};
|
||||
for (const key in formatResponse) {
|
||||
const output = outputs.find((item) => item.key === key);
|
||||
const output = node.outputs.find((item) => item.key === key);
|
||||
if (!output) continue;
|
||||
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
||||
}
|
||||
@@ -213,7 +270,6 @@ async function fetchData({
|
||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
||||
url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
},
|
||||
timeout: timeout * 1000,
|
||||
|
||||
@@ -44,7 +44,7 @@ ${content.slice(0, 100)}${content.length > 100 ? '......' : ''}
|
||||
export const dispatchReadFiles = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
requestOrigin,
|
||||
teamId,
|
||||
runningAppInfo: { teamId },
|
||||
histories,
|
||||
chatConfig,
|
||||
params: { fileUrlList = [] }
|
||||
|
||||
@@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
||||
|
||||
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
|
||||
let {
|
||||
app: { _id: appId },
|
||||
runningAppInfo: { id: appId },
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
variables,
|
||||
|
||||
@@ -22,6 +22,7 @@ export type DispatchFlowResponse = {
|
||||
};
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: number;
|
||||
newVariables: Record<string, string>;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
WorkflowIOValueTypeEnum,
|
||||
NodeOutputKeyEnum
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import {
|
||||
RuntimeEdgeItemType,
|
||||
SystemVariablesType
|
||||
} 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';
|
||||
@@ -144,8 +147,9 @@ export const removeSystemVariable = (variables: Record<string, any>) => {
|
||||
|
||||
return copyVariables;
|
||||
};
|
||||
export const filterSystemVariables = (variables: Record<string, any>) => {
|
||||
export const filterSystemVariables = (variables: Record<string, any>): SystemVariablesType => {
|
||||
return {
|
||||
userId: variables.userId,
|
||||
appId: variables.appId,
|
||||
chatId: variables.chatId,
|
||||
responseChatItemId: variables.responseChatItemId,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { countPromptTokens } from '../../common/string/tiktoken/index';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
getPluginInputsFromStoreNodes,
|
||||
getPluginRunContent
|
||||
} from '@fastgpt/global/core/app/plugin/utils';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { UserChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
|
||||
/* filter search result */
|
||||
export const filterSearchResultsByMaxChars = async (
|
||||
@@ -23,3 +31,25 @@ export const filterSearchResultsByMaxChars = async (
|
||||
|
||||
return results.length === 0 ? list.slice(0, 1) : results;
|
||||
};
|
||||
|
||||
/* Get plugin runtime input user query */
|
||||
export const getPluginRunUserQuery = (
|
||||
nodes: StoreNodeItemType[],
|
||||
variables: Record<string, any>
|
||||
): UserChatItemType & { dataId: string } => {
|
||||
return {
|
||||
dataId: getNanoid(24),
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: getPluginRunContent({
|
||||
pluginInputs: getPluginInputsFromStoreNodes(nodes),
|
||||
variables
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25,22 +25,26 @@ export const authPluginByTmbId = async ({
|
||||
}) => {
|
||||
const { source } = await splitCombinePluginId(appId);
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
await authAppByTmbId({
|
||||
const { app } = await authAppByTmbId({
|
||||
appId,
|
||||
tmbId,
|
||||
per
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
};
|
||||
|
||||
export const authAppByTmbId = async ({
|
||||
tmbId,
|
||||
appId,
|
||||
per
|
||||
per,
|
||||
isRoot
|
||||
}: {
|
||||
tmbId: string;
|
||||
appId: string;
|
||||
per: PermissionValueType;
|
||||
isRoot?: boolean;
|
||||
}): Promise<{
|
||||
app: AppDetailType;
|
||||
}> => {
|
||||
@@ -53,6 +57,14 @@ export const authAppByTmbId = async ({
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
return {
|
||||
...app,
|
||||
defaultPermission: app.defaultPermission,
|
||||
permission: new AppPermission({ isOwner: true })
|
||||
};
|
||||
}
|
||||
|
||||
if (String(app.teamId) !== teamId) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
@@ -134,7 +146,8 @@ export const authApp = async ({
|
||||
const { app } = await authAppByTmbId({
|
||||
tmbId,
|
||||
appId,
|
||||
per
|
||||
per,
|
||||
isRoot: result.isRoot
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -78,13 +78,18 @@ export const delResourcePermission = ({
|
||||
|
||||
/* 下面代码等迁移 */
|
||||
/* create token */
|
||||
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
|
||||
export function createJWT(user: {
|
||||
_id?: string;
|
||||
team?: { teamId?: string; tmbId: string };
|
||||
isRoot?: boolean;
|
||||
}) {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: String(user._id),
|
||||
teamId: String(user.team?.teamId),
|
||||
tmbId: String(user.team?.tmbId),
|
||||
isRoot: user.isRoot,
|
||||
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7
|
||||
},
|
||||
key
|
||||
@@ -98,6 +103,7 @@ export function authJWT(token: string) {
|
||||
userId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
isRoot: boolean;
|
||||
}>((resolve, reject) => {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
@@ -110,7 +116,8 @@ export function authJWT(token: string) {
|
||||
resolve({
|
||||
userId: decoded.userId,
|
||||
teamId: decoded.teamId || '',
|
||||
tmbId: decoded.tmbId
|
||||
tmbId: decoded.tmbId,
|
||||
isRoot: decoded.isRoot
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -183,7 +190,7 @@ export async function parseHeaderCert({
|
||||
|
||||
const { cookie, token, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType;
|
||||
|
||||
const { uid, teamId, tmbId, appId, openApiKey, authType } = await (async () => {
|
||||
const { uid, teamId, tmbId, appId, openApiKey, authType, isRoot } = await (async () => {
|
||||
if (authApiKey && authorization) {
|
||||
// apikey from authorization
|
||||
const authResponse = await parseAuthorization(authorization);
|
||||
@@ -205,7 +212,8 @@ export async function parseHeaderCert({
|
||||
tmbId: res.tmbId,
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.token
|
||||
authType: AuthUserTypeEnum.token,
|
||||
isRoot: res.isRoot
|
||||
};
|
||||
}
|
||||
if (authRoot && rootkey) {
|
||||
@@ -217,7 +225,8 @@ export async function parseHeaderCert({
|
||||
tmbId: '',
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.root
|
||||
authType: AuthUserTypeEnum.root,
|
||||
isRoot: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -234,7 +243,8 @@ export async function parseHeaderCert({
|
||||
tmbId: String(tmbId),
|
||||
appId,
|
||||
authType,
|
||||
apikey: openApiKey
|
||||
apikey: openApiKey,
|
||||
isRoot: !!isRoot
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
export const authDatasetByTmbId = async ({
|
||||
tmbId,
|
||||
datasetId,
|
||||
per
|
||||
per,
|
||||
isRoot = false
|
||||
}: {
|
||||
tmbId: string;
|
||||
datasetId: string;
|
||||
per: PermissionValueType;
|
||||
isRoot?: boolean;
|
||||
}): Promise<{
|
||||
dataset: DatasetSchemaType & {
|
||||
permission: DatasetPermission;
|
||||
@@ -44,6 +46,15 @@ export const authDatasetByTmbId = async ({
|
||||
return Promise.reject(DatasetErrEnum.unExist);
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
return {
|
||||
...dataset,
|
||||
permission: new DatasetPermission({
|
||||
isOwner: true
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
if (String(dataset.teamId) !== teamId) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
}
|
||||
@@ -79,13 +90,15 @@ export const authDatasetByTmbId = async ({
|
||||
const { dataset: parent } = await authDatasetByTmbId({
|
||||
tmbId,
|
||||
datasetId: dataset.parentId,
|
||||
per
|
||||
per,
|
||||
isRoot
|
||||
});
|
||||
|
||||
const Per = new DatasetPermission({
|
||||
per: parent.permission.value,
|
||||
isOwner
|
||||
});
|
||||
|
||||
return {
|
||||
Per,
|
||||
defaultPermission: parent.defaultPermission
|
||||
@@ -131,7 +144,8 @@ export const authDataset = async ({
|
||||
const { dataset } = await authDatasetByTmbId({
|
||||
tmbId,
|
||||
datasetId,
|
||||
per
|
||||
per,
|
||||
isRoot: result.isRoot
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -144,15 +158,17 @@ export const authDataset = async ({
|
||||
export async function authDatasetCollection({
|
||||
collectionId,
|
||||
per = NullPermission,
|
||||
isRoot = false,
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
collectionId: string;
|
||||
isRoot?: boolean;
|
||||
}): Promise<
|
||||
AuthResponseType<DatasetPermission> & {
|
||||
collection: CollectionWithDatasetType;
|
||||
}
|
||||
> {
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { teamId, tmbId, isRoot: isRootFromHeader } = await parseHeaderCert(props);
|
||||
const collection = await getCollectionWithDataset(collectionId);
|
||||
|
||||
if (!collection) {
|
||||
@@ -162,7 +178,8 @@ export async function authDatasetCollection({
|
||||
const { dataset } = await authDatasetByTmbId({
|
||||
tmbId,
|
||||
datasetId: collection.datasetId._id,
|
||||
per
|
||||
per,
|
||||
isRoot: isRootFromHeader || isRoot
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -184,7 +201,7 @@ export async function authDatasetFile({
|
||||
file: DatasetFileSchema;
|
||||
}
|
||||
> {
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { teamId, tmbId, isRoot } = await parseHeaderCert(props);
|
||||
|
||||
const [file, collection] = await Promise.all([
|
||||
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
|
||||
@@ -206,7 +223,8 @@ export async function authDatasetFile({
|
||||
const { permission } = await authDatasetCollection({
|
||||
...props,
|
||||
collectionId: collection._id,
|
||||
per
|
||||
per,
|
||||
isRoot
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { AuthModeType, AuthResponseType } from '../type';
|
||||
import { NullPermission } from '@fastgpt/global/support/permission/constant';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
|
||||
/* auth user role */
|
||||
export async function authUserPer(props: AuthModeType): Promise<
|
||||
@@ -14,6 +15,15 @@ export async function authUserPer(props: AuthModeType): Promise<
|
||||
const result = await parseHeaderCert(props);
|
||||
const tmb = await getTmbInfoByTmbId({ tmbId: result.tmbId });
|
||||
|
||||
if (result.isRoot) {
|
||||
return {
|
||||
...result,
|
||||
permission: new TeamPermission({
|
||||
isOwner: true
|
||||
}),
|
||||
tmb
|
||||
};
|
||||
}
|
||||
if (!tmb.permission.checkPer(props.per ?? NullPermission)) {
|
||||
return Promise.reject(TeamErrEnum.unAuthTeam);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,19 @@ import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/cons
|
||||
|
||||
export const userCollectionName = 'users';
|
||||
|
||||
const defaultAvatars = [
|
||||
'/imgs/avatar/RoyalBlueAvatar.svg',
|
||||
'/imgs/avatar/PurpleAvatar.svg',
|
||||
'/imgs/avatar/AdoraAvatar.svg',
|
||||
'/imgs/avatar/OrangeAvatar.svg',
|
||||
'/imgs/avatar/RedAvatar.svg',
|
||||
'/imgs/avatar/GrayModernAvatar.svg',
|
||||
'/imgs/avatar/TealAvatar.svg',
|
||||
'/imgs/avatar/GreenAvatar.svg',
|
||||
'/imgs/avatar/BrightBlueAvatar.svg',
|
||||
'/imgs/avatar/BlueAvatar.svg'
|
||||
];
|
||||
|
||||
const UserSchema = new Schema({
|
||||
status: {
|
||||
type: String,
|
||||
@@ -34,7 +47,7 @@ const UserSchema = new Schema({
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: '/icon/human.svg'
|
||||
default: defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
|
||||
},
|
||||
inviterId: {
|
||||
// 谁邀请注册的
|
||||
|
||||
@@ -50,7 +50,7 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const initTeamStandardPlan2Free = async ({
|
||||
export const initTeamFreePlan = async ({
|
||||
teamId,
|
||||
session
|
||||
}: {
|
||||
@@ -59,23 +59,28 @@ export const initTeamStandardPlan2Free = async ({
|
||||
}) => {
|
||||
const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100;
|
||||
|
||||
const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard });
|
||||
const freePlan = await MongoTeamSub.findOne({
|
||||
teamId,
|
||||
type: SubTypeEnum.standard,
|
||||
currentSubLevel: StandardSubLevelEnum.free
|
||||
});
|
||||
|
||||
if (teamStandardSub) {
|
||||
teamStandardSub.currentMode = SubModeEnum.month;
|
||||
teamStandardSub.nextMode = SubModeEnum.month;
|
||||
teamStandardSub.startTime = new Date();
|
||||
teamStandardSub.expiredTime = addMonths(new Date(), 1);
|
||||
// Reset one month free plan
|
||||
if (freePlan) {
|
||||
freePlan.currentMode = SubModeEnum.month;
|
||||
freePlan.nextMode = SubModeEnum.month;
|
||||
freePlan.startTime = new Date();
|
||||
freePlan.expiredTime = addMonths(new Date(), 1);
|
||||
|
||||
teamStandardSub.currentSubLevel = StandardSubLevelEnum.free;
|
||||
teamStandardSub.nextSubLevel = StandardSubLevelEnum.free;
|
||||
freePlan.currentSubLevel = StandardSubLevelEnum.free;
|
||||
freePlan.nextSubLevel = StandardSubLevelEnum.free;
|
||||
|
||||
teamStandardSub.totalPoints = freePoints;
|
||||
teamStandardSub.surplusPoints =
|
||||
teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0
|
||||
? teamStandardSub.surplusPoints + freePoints
|
||||
freePlan.totalPoints = freePoints;
|
||||
freePlan.surplusPoints =
|
||||
freePlan.surplusPoints && freePlan.surplusPoints < 0
|
||||
? freePlan.surplusPoints + freePoints
|
||||
: freePoints;
|
||||
return teamStandardSub.save({ session });
|
||||
return freePlan.save({ session });
|
||||
}
|
||||
|
||||
return MongoTeamSub.create(
|
||||
@@ -123,13 +128,14 @@ export const getTeamPlanStatus = async ({
|
||||
|
||||
// Free user, first login after expiration. The free subscription plan will be reset
|
||||
if (
|
||||
standardPlan &&
|
||||
standardPlan.expiredTime &&
|
||||
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
|
||||
dayjs(standardPlan.expiredTime).isBefore(new Date())
|
||||
(standardPlan &&
|
||||
standardPlan.expiredTime &&
|
||||
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
|
||||
dayjs(standardPlan.expiredTime).isBefore(new Date())) ||
|
||||
teamStandardPlans.length === 0
|
||||
) {
|
||||
console.log('Init free stand plan', { teamId });
|
||||
await initTeamStandardPlan2Free({ teamId });
|
||||
await initTeamFreePlan({ teamId });
|
||||
return getTeamPlanStatus({ teamId });
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
export const iconPaths = {
|
||||
book: () => import('./icons/book.svg'),
|
||||
visible: () => import('./icons/visible.svg'),
|
||||
change: () => import('./icons/change.svg'),
|
||||
chatSend: () => import('./icons/chatSend.svg'),
|
||||
configmap: () => import('./icons/configmap.svg'),
|
||||
closeSolid: () => import('./icons/closeSolid.svg'),
|
||||
collectionLight: () => import('./icons/collectionLight.svg'),
|
||||
collectionSolid: () => import('./icons/collectionSolid.svg'),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.18118 2.74428L4.49007 2.74428C5.17179 2.74427 5.72618 2.74427 6.17608 2.78102C6.64077 2.81899 7.0558 2.89966 7.44194 3.09642C7.85435 3.30655 8.21445 3.60159 8.50012 3.95943C8.78579 3.60159 9.14588 3.30655 9.55829 3.09642C9.94444 2.89966 10.3595 2.81899 10.8242 2.78102C11.2741 2.74427 11.8284 2.74427 12.5102 2.74428L12.8191 2.74428C13.1499 2.74426 13.4396 2.74425 13.6792 2.76382C13.9333 2.78459 14.1925 2.83086 14.4436 2.95881C14.8139 3.14753 15.1151 3.44864 15.3038 3.81901C15.4317 4.07013 15.478 4.32926 15.4988 4.58343C15.5183 4.82296 15.5183 5.11271 15.5183 5.44351V11.5565C15.5183 11.8873 15.5183 12.177 15.4988 12.4166C15.478 12.6707 15.4317 12.9299 15.3038 13.181C15.1151 13.5514 14.8139 13.8525 14.4436 14.0412C14.1925 14.1691 13.9333 14.2154 13.6792 14.2362C13.4396 14.2557 13.1499 14.2557 12.819 14.2557H4.18119C3.85038 14.2557 3.56062 14.2557 3.32108 14.2362C3.06691 14.2154 2.80778 14.1691 2.55666 14.0412C2.1863 13.8525 1.88518 13.5514 1.69647 13.181C1.56852 12.9299 1.52225 12.6707 1.50148 12.4166C1.48191 12.177 1.48192 11.8873 1.48193 11.5565V5.44352C1.48192 5.11272 1.48191 4.82296 1.50148 4.58343C1.52225 4.32926 1.56852 4.07013 1.69647 3.81901C1.88518 3.44864 2.1863 3.14753 2.55666 2.95881C2.80778 2.83086 3.06691 2.78459 3.32108 2.76382C3.56062 2.74425 3.85037 2.74426 4.18118 2.74428ZM7.79425 7.49003C7.79425 6.77134 7.7937 6.27477 7.76219 5.88914C7.73136 5.51176 7.67443 5.3032 7.59598 5.14924C7.42158 4.80696 7.1433 4.52869 6.80103 4.35429C6.64706 4.27584 6.43851 4.21891 6.06112 4.18808C5.6755 4.15657 5.17893 4.15602 4.46024 4.15602H4.20775C3.84258 4.15602 3.61115 4.15657 3.43604 4.17088C3.26918 4.18451 3.21652 4.20704 3.19758 4.21669C3.09285 4.27005 3.0077 4.3552 2.95434 4.45993C2.94469 4.47886 2.92217 4.53152 2.90853 4.69839C2.89423 4.87349 2.89368 5.10492 2.89368 5.47009V11.5299C2.89368 11.8951 2.89423 12.1265 2.90853 12.3016C2.92217 12.4685 2.94469 12.5211 2.95434 12.5401C3.0077 12.6448 3.09285 12.73 3.19758 12.7833C3.21652 12.793 3.26918 12.8155 3.43604 12.8291C3.61115 12.8434 3.84258 12.844 4.20775 12.844H7.79425V7.49003ZM9.20599 12.844V7.49003C9.20599 6.77134 9.20654 6.27477 9.23805 5.88914C9.26888 5.51176 9.32581 5.3032 9.40425 5.14924C9.57865 4.80696 9.85693 4.52869 10.1992 4.35429C10.3532 4.27584 10.5617 4.21891 10.9391 4.18808C11.3247 4.15657 11.8213 4.15602 12.54 4.15602H12.7925C13.1577 4.15602 13.3891 4.15657 13.5642 4.17088C13.7311 4.18451 13.7837 4.20704 13.8027 4.21669C13.9074 4.27005 13.9925 4.3552 14.0459 4.45993C14.0555 4.47886 14.0781 4.53152 14.0917 4.69839C14.106 4.87349 14.1066 5.10492 14.1066 5.47009V11.5299C14.1066 11.8951 14.106 12.1265 14.0917 12.3016C14.0781 12.4685 14.0555 12.5211 14.0459 12.5401C13.9925 12.6448 13.9074 12.73 13.8027 12.7833C13.7837 12.793 13.7311 12.8155 13.5642 12.8291C13.3891 12.8434 13.1577 12.844 12.7925 12.844H9.20599Z" fill="#2B5FD9"/>
|
||||
<svg viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.18118 2.74428L4.49007 2.74428C5.17179 2.74427 5.72618 2.74427 6.17608 2.78102C6.64077 2.81899 7.0558 2.89966 7.44194 3.09642C7.85435 3.30655 8.21445 3.60159 8.50012 3.95943C8.78579 3.60159 9.14588 3.30655 9.55829 3.09642C9.94444 2.89966 10.3595 2.81899 10.8242 2.78102C11.2741 2.74427 11.8284 2.74427 12.5102 2.74428L12.8191 2.74428C13.1499 2.74426 13.4396 2.74425 13.6792 2.76382C13.9333 2.78459 14.1925 2.83086 14.4436 2.95881C14.8139 3.14753 15.1151 3.44864 15.3038 3.81901C15.4317 4.07013 15.478 4.32926 15.4988 4.58343C15.5183 4.82296 15.5183 5.11271 15.5183 5.44351V11.5565C15.5183 11.8873 15.5183 12.177 15.4988 12.4166C15.478 12.6707 15.4317 12.9299 15.3038 13.181C15.1151 13.5514 14.8139 13.8525 14.4436 14.0412C14.1925 14.1691 13.9333 14.2154 13.6792 14.2362C13.4396 14.2557 13.1499 14.2557 12.819 14.2557H4.18119C3.85038 14.2557 3.56062 14.2557 3.32108 14.2362C3.06691 14.2154 2.80778 14.1691 2.55666 14.0412C2.1863 13.8525 1.88518 13.5514 1.69647 13.181C1.56852 12.9299 1.52225 12.6707 1.50148 12.4166C1.48191 12.177 1.48192 11.8873 1.48193 11.5565V5.44352C1.48192 5.11272 1.48191 4.82296 1.50148 4.58343C1.52225 4.32926 1.56852 4.07013 1.69647 3.81901C1.88518 3.44864 2.1863 3.14753 2.55666 2.95881C2.80778 2.83086 3.06691 2.78459 3.32108 2.76382C3.56062 2.74425 3.85037 2.74426 4.18118 2.74428ZM7.79425 7.49003C7.79425 6.77134 7.7937 6.27477 7.76219 5.88914C7.73136 5.51176 7.67443 5.3032 7.59598 5.14924C7.42158 4.80696 7.1433 4.52869 6.80103 4.35429C6.64706 4.27584 6.43851 4.21891 6.06112 4.18808C5.6755 4.15657 5.17893 4.15602 4.46024 4.15602H4.20775C3.84258 4.15602 3.61115 4.15657 3.43604 4.17088C3.26918 4.18451 3.21652 4.20704 3.19758 4.21669C3.09285 4.27005 3.0077 4.3552 2.95434 4.45993C2.94469 4.47886 2.92217 4.53152 2.90853 4.69839C2.89423 4.87349 2.89368 5.10492 2.89368 5.47009V11.5299C2.89368 11.8951 2.89423 12.1265 2.90853 12.3016C2.92217 12.4685 2.94469 12.5211 2.95434 12.5401C3.0077 12.6448 3.09285 12.73 3.19758 12.7833C3.21652 12.793 3.26918 12.8155 3.43604 12.8291C3.61115 12.8434 3.84258 12.844 4.20775 12.844H7.79425V7.49003ZM9.20599 12.844V7.49003C9.20599 6.77134 9.20654 6.27477 9.23805 5.88914C9.26888 5.51176 9.32581 5.3032 9.40425 5.14924C9.57865 4.80696 9.85693 4.52869 10.1992 4.35429C10.3532 4.27584 10.5617 4.21891 10.9391 4.18808C11.3247 4.15657 11.8213 4.15602 12.54 4.15602H12.7925C13.1577 4.15602 13.3891 4.15657 13.5642 4.17088C13.7311 4.18451 13.7837 4.20704 13.8027 4.21669C13.9074 4.27005 13.9925 4.3552 14.0459 4.45993C14.0555 4.47886 14.0781 4.53152 14.0917 4.69839C14.106 4.87349 14.1066 5.10492 14.1066 5.47009V11.5299C14.1066 11.8951 14.106 12.1265 14.0917 12.3016C14.0781 12.4685 14.0555 12.5211 14.0459 12.5401C13.9925 12.6448 13.9074 12.73 13.8027 12.7833C13.7837 12.793 13.7311 12.8155 13.5642 12.8291C13.3891 12.8434 13.1577 12.844 12.7925 12.844H9.20599Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
|
||||
<path
|
||||
d="M4.25845 0.738983C4.25845 0.606996 4.15146 0.5 4.01947 0.5C2.11725 0.5 0.575195 2.04205 0.575195 3.94428V12.0557C0.575195 13.9579 2.11725 15.5 4.01947 15.5H12.1309C14.0331 15.5 15.5752 13.9579 15.5752 12.0557V3.94428C15.5752 2.04205 14.0331 0.5 12.1309 0.5H10.4017C9.98744 0.5 9.65166 0.835786 9.65166 1.25C9.65166 1.66421 9.98744 2 10.4017 2H12.1309C13.2047 2 14.0752 2.87048 14.0752 3.94428V12.0557C14.0752 13.1295 13.2047 14 12.1309 14H4.01947C2.94568 14 2.0752 13.1295 2.0752 12.0557V3.94428C2.0752 2.87048 2.94568 2 4.01947 2C4.15146 2 4.25845 1.893 4.25845 1.76102V0.738983Z" />
|
||||
<path
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
4
packages/web/components/common/Icon/icons/configmap.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3.63151C12.8097 3.61563 12.5318 3.61309 12.0118 3.61309H8.8C7.94442 3.61309 7.35921 3.61378 6.90608 3.64853C6.46289 3.68252 6.23164 3.7446 6.0695 3.82215C5.68754 4.00483 5.38889 4.29054 5.20585 4.62774C5.13457 4.75906 5.07214 4.95341 5.03711 5.35592C5.00088 5.7721 5 6.31265 5 7.11879V16.8811C5 17.6873 5.00088 18.2278 5.03711 18.644C5.07214 19.0465 5.13457 19.2409 5.20585 19.3722C5.38889 19.7094 5.68754 19.9951 6.0695 20.1778C6.23164 20.2553 6.46289 20.3174 6.90608 20.3514C7.35921 20.3861 7.94442 20.3868 8.8 20.3868H11.3166V20.3831H12.0402C12.5935 20.3831 13.0421 20.8317 13.0421 21.385C13.0421 21.9383 12.5935 22.3868 12.0402 22.3868H8.76082C7.95401 22.3868 7.29182 22.3869 6.75314 22.3455C6.19582 22.3028 5.68616 22.2114 5.20656 21.982C4.45954 21.6247 3.84031 21.0488 3.44811 20.3263C3.19241 19.8552 3.09135 19.3541 3.04464 18.8174C2.99997 18.3042 2.99999 17.6761 3 16.9243V7.07562C2.99999 6.32377 2.99997 5.69569 3.04464 5.18249C3.09135 4.64583 3.19241 4.1447 3.44811 3.67363C3.84031 2.95108 4.45955 2.37518 5.20656 2.0179C5.68616 1.78851 6.19582 1.69713 6.75314 1.65438C7.29183 1.61307 7.95404 1.61308 8.76087 1.61309L12.1104 1.61306C12.7488 1.6128 13.2216 1.61261 13.6775 1.71536C13.9143 1.76871 14.1455 1.84257 14.3684 1.93605C14.3845 1.94245 14.4004 1.94926 14.4162 1.95646C14.5556 2.01705 14.6916 2.08538 14.8235 2.16124C15.2292 2.3946 15.5693 2.71422 16.0188 3.13657L19.3536 6.26681C19.7996 6.68508 20.1428 7.00687 20.3953 7.39362C20.4782 7.52058 20.5532 7.65209 20.6199 7.78747C20.6275 7.8022 20.6347 7.81715 20.6416 7.8323C20.7445 8.04841 20.8265 8.27406 20.8859 8.50648C21.0007 8.95522 21.0004 9.42011 21 10.0095L21.0001 12.9707C21.0001 13.524 20.5515 13.9725 19.9982 13.9725C19.4449 13.9725 18.9964 13.524 18.9964 12.9707V12.2471H19V10.1115C19 9.65934 18.9973 9.41026 18.985 9.24521L15.5694 9.24522C15.3159 9.24524 15.0723 9.24526 14.8667 9.2295C14.6429 9.21233 14.3802 9.1721 14.1145 9.04503C13.7438 8.86773 13.4306 8.57903 13.2301 8.20974C13.0834 7.93947 13.0369 7.67016 13.0174 7.44655C12.9999 7.24586 13 7.01037 13 6.77572L13 3.63151ZM15 4.92332V6.74331C15 6.99036 15.0007 7.13329 15.007 7.23432L15.0197 7.23535C15.1393 7.24453 15.3045 7.24521 15.6 7.24521H17.4736L15 4.92332Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6589 16.1948C17.9477 15.0725 19.5415 15.0725 19.8304 16.1948C19.9868 16.8028 20.6122 17.1639 21.217 16.9953C22.3333 16.6843 23.1303 18.0647 22.3028 18.8759C21.8544 19.3155 21.8544 20.0375 22.3028 20.4771C23.1303 21.2883 22.3333 22.6687 21.217 22.3577C20.6122 22.1891 19.9868 22.5502 19.8304 23.1582C19.5415 24.2805 17.9477 24.2805 17.6589 23.1582C17.5024 22.5502 16.8771 22.1891 16.2722 22.3577C15.1559 22.6687 14.3589 21.2883 15.1865 20.4771C15.6348 20.0375 15.6348 19.3155 15.1865 18.8759C14.3589 18.0647 15.1559 16.6843 16.2722 16.9953C16.8771 17.1639 17.5024 16.8028 17.6589 16.1948ZM20.1433 19.6765C20.1433 20.449 19.5171 21.0752 18.7446 21.0752C17.9721 21.0752 17.3459 20.449 17.3459 19.6765C17.3459 18.904 17.9721 18.2778 18.7446 18.2778C19.5171 18.2778 20.1433 18.904 20.1433 19.6765Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1 +1,4 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689491332665" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5892" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M393.2 701.7c-8.2 0-16.4-3.1-22.6-9.4-12.5-12.5-12.5-32.8 0-45.3l115.3-115.3c14-14 36.9-14 50.9 0L652.1 647c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-95.5-95.5-95.5 95.5c-6.2 6.2-14.4 9.4-22.6 9.4z" fill="" p-id="5893"></path><path d="M511.3 921.1c-17.7 0-32-14.3-32-32v-276c0-17.7 14.3-32 32-32s32 14.3 32 32v276c0 17.6-14.3 32-32 32z" fill="" p-id="5894"></path><path d="M732.7 784.9c-17.7 0-32-14.3-32-32s14.3-32 32-32c90.6 0 164.3-73.7 164.3-164.3 0-82.9-61.9-153-144-163.1l-22.7-2.8-4.7-22.4c-20.8-99.9-110.1-172.4-212.4-172.4-102.2 0-191.5 72.5-212.4 172.4l-4.7 22.4-22.7 2.8c-82.1 10.1-144 80.2-144 163.1 0 90.6 73.7 164.3 164.3 164.3 17.7 0 32 14.3 32 32s-14.3 32-32 32c-61 0-118.3-23.8-161.5-66.9-43.1-43.1-66.9-100.5-66.9-161.5 0-107.6 75.2-199.7 178.2-222.8 15.8-53.8 47.7-102.2 91.3-138.1 50.1-41.2 113.4-63.9 178.3-63.9s128.3 22.7 178.3 63.9c43.6 35.9 75.5 84.3 91.3 138.1C885.9 356.8 961 448.9 961 556.5c0 61-23.8 118.3-66.9 161.5-43.1 43.1-100.4 66.9-161.4 66.9z" fill="" p-id="5895"></path></svg>
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9689 12.7335C13.8022 12.6748 15.2703 11.1701 15.2703 9.32256C15.2703 7.72206 14.1686 6.37888 12.6821 6.01009C12.1325 3.93852 10.2445 2.41187 8.00003 2.41187C5.75552 2.41187 3.86757 3.93852 3.31794 6.01009C1.83149 6.37888 0.729736 7.72206 0.729736 9.32256C0.729736 11.1827 2.2179 12.6952 4.06858 12.7345V12.7353H4.33524C4.70343 12.7353 5.00191 12.4368 5.00191 12.0686C5.00191 11.7004 4.70343 11.402 4.33524 11.402H4.14248C2.99405 11.402 2.06307 10.471 2.06307 9.32256C2.06307 8.34915 2.73317 7.52893 3.63901 7.30419L4.40444 7.11428L4.60668 6.35202C5.00513 4.85028 6.37523 3.7452 8.00003 3.7452C9.62484 3.7452 10.9949 4.85028 11.3934 6.35202L11.5956 7.11428L12.361 7.30419C13.2669 7.52893 13.937 8.34915 13.937 9.32256C13.937 10.4672 13.0121 11.3958 11.8689 11.4019L11.7009 11.402C11.3327 11.402 11.0342 11.7004 11.0342 12.0686C11.0342 12.4368 11.3327 12.7353 11.7009 12.7353H11.9689V12.7335Z" />
|
||||
<path d="M6.17498 8.73195L7.50552 7.40141C7.52051 7.38484 7.53633 7.36902 7.5529 7.35403L7.60322 7.30371L7.6041 7.30283C7.82262 7.08431 8.17682 7.08417 8.39551 7.30241C8.39595 7.30284 8.39638 7.30327 8.39682 7.30371L8.44704 7.35393C8.46368 7.36899 8.47957 7.38487 8.49462 7.40151L9.82417 8.73107C10.0433 8.95021 10.0433 9.30552 9.82417 9.52466C9.60503 9.74381 9.24972 9.74381 9.03058 9.52466L8.66669 9.16077V13.0339C8.66669 13.402 8.36821 13.7005 8.00002 13.7005C7.63183 13.7005 7.33336 13.402 7.33336 13.0339V9.15724L6.96682 9.52378C6.74816 9.74244 6.39364 9.74244 6.17498 9.52378C5.95632 9.30512 5.95632 8.95061 6.17498 8.73195Z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
3
packages/web/components/common/Icon/icons/visible.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.00011 6.3335C8.44214 6.3335 8.86606 6.50909 9.17863 6.82165C9.49119 7.13421 9.66678 7.55814 9.66678 8.00016C9.66678 8.44219 9.49119 8.86611 9.17863 9.17867C8.86606 9.49124 8.44214 9.66683 8.00011 9.66683C7.55809 9.66683 7.13416 9.49124 6.8216 9.17867C6.50904 8.86611 6.33345 8.44219 6.33345 8.00016C6.33345 7.55814 6.50904 7.13421 6.8216 6.82165C7.13416 6.50909 7.55809 6.3335 8.00011 6.3335ZM8.00011 3.8335C10.4794 3.8335 12.6356 5.20988 13.7537 7.23977C13.862 7.43633 13.9161 7.53461 13.9567 7.73345C13.9835 7.86447 13.9835 8.13586 13.9567 8.26688C13.9161 8.46572 13.862 8.564 13.7537 8.76055C12.6356 10.7905 10.4794 12.1668 8.00011 12.1668C5.52085 12.1668 3.36466 10.7905 2.2465 8.76055C2.13823 8.564 2.0841 8.46572 2.0435 8.26688C2.01675 8.13586 2.01675 7.86447 2.0435 7.73345C2.0841 7.53461 2.13823 7.43633 2.2465 7.23977C3.36466 5.20988 5.52085 3.8335 8.00011 3.8335ZM3.64644 7.11141C3.49828 7.30756 3.4242 7.40563 3.35714 7.65897C3.31559 7.81594 3.31559 8.18438 3.35714 8.34135C3.4242 8.5947 3.49828 8.69277 3.64644 8.88891C4.04722 9.41949 4.54344 9.87474 5.1126 10.2297C5.97881 10.77 6.97922 11.0565 8.00011 11.0565C9.021 11.0565 10.0214 10.77 10.8876 10.2297C11.4568 9.87474 11.953 9.41949 12.3538 8.88891C12.5019 8.69277 12.576 8.5947 12.6431 8.34135C12.6846 8.18438 12.6846 7.81594 12.6431 7.65897C12.576 7.40563 12.5019 7.30756 12.3538 7.11141C11.953 6.58083 11.4568 6.12558 10.8876 5.77058C10.0214 5.2303 9.021 4.94387 8.00011 4.94387C6.97922 4.94387 5.97881 5.2303 5.1126 5.77058C4.54344 6.12558 4.04722 6.58083 3.64644 7.11141Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -17,39 +17,40 @@ import { Box, Flex } from '@chakra-ui/react';
|
||||
import styles from './index.module.scss';
|
||||
import { EditorState, LexicalEditor } from 'lexical';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type';
|
||||
import {
|
||||
EditorVariableLabelPickerType,
|
||||
EditorVariablePickerType
|
||||
} from '../../Textarea/PromptEditor/type';
|
||||
import { VariableNode } from '../../Textarea/PromptEditor/plugins/VariablePlugin/node';
|
||||
import { textToEditorState } from '../../Textarea/PromptEditor/utils';
|
||||
import DropDownMenu from '../../Textarea/PromptEditor/modules/DropDownMenu';
|
||||
import { SingleLinePlugin } from '../../Textarea/PromptEditor/plugins/SingleLinePlugin';
|
||||
import OnBlurPlugin from '../../Textarea/PromptEditor/plugins/OnBlurPlugin';
|
||||
import VariablePlugin from '../../Textarea/PromptEditor/plugins/VariablePlugin';
|
||||
import VariablePickerPlugin from '../../Textarea/PromptEditor/plugins/VariablePickerPlugin';
|
||||
import FocusPlugin from '../../Textarea/PromptEditor/plugins/FocusPlugin';
|
||||
import VariableLabelPlugin from '../../Textarea/PromptEditor/plugins/VariableLabelPlugin';
|
||||
import { VariableLabelNode } from '../../Textarea/PromptEditor/plugins/VariableLabelPlugin/node';
|
||||
import VariableLabelPickerPlugin from '../../Textarea/PromptEditor/plugins/VariableLabelPickerPlugin';
|
||||
|
||||
export default function Editor({
|
||||
h = 40,
|
||||
hasVariablePlugin = true,
|
||||
hasDropDownPlugin = false,
|
||||
variables,
|
||||
variableLabels,
|
||||
onChange,
|
||||
onBlur,
|
||||
value,
|
||||
currentValue,
|
||||
placeholder = '',
|
||||
setDropdownValue,
|
||||
updateTrigger
|
||||
}: {
|
||||
h?: number;
|
||||
hasVariablePlugin?: boolean;
|
||||
hasDropDownPlugin?: boolean;
|
||||
variables: EditorVariablePickerType[];
|
||||
variableLabels: EditorVariableLabelPickerType[];
|
||||
onChange?: (editorState: EditorState, editor: LexicalEditor) => void;
|
||||
onBlur?: (editor: LexicalEditor) => void;
|
||||
value?: string;
|
||||
currentValue?: string;
|
||||
placeholder?: string;
|
||||
setDropdownValue?: (value: string) => void;
|
||||
updateTrigger?: boolean;
|
||||
}) {
|
||||
const [key, setKey] = useState(getNanoid(6));
|
||||
@@ -58,7 +59,7 @@ export default function Editor({
|
||||
|
||||
const initialConfig = {
|
||||
namespace: 'HttpInput',
|
||||
nodes: [VariableNode],
|
||||
nodes: [VariableNode, VariableLabelNode],
|
||||
editorState: textToEditorState(value),
|
||||
onError: (error: Error) => {
|
||||
throw error;
|
||||
@@ -75,16 +76,6 @@ export default function Editor({
|
||||
setFocus(false);
|
||||
}, [updateTrigger]);
|
||||
|
||||
const dropdownVariables = useMemo(
|
||||
() =>
|
||||
variables.filter((item) => {
|
||||
const key = item.key.toLowerCase();
|
||||
const current = currentValue?.toLowerCase();
|
||||
return key.includes(current || '') && item.key !== currentValue;
|
||||
}),
|
||||
[currentValue, variables]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
position={'relative'}
|
||||
@@ -133,14 +124,12 @@ export default function Editor({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{hasVariablePlugin ? <VariablePickerPlugin variables={variables} /> : ''}
|
||||
<VariablePlugin variables={variables} />
|
||||
<VariableLabelPlugin variables={variableLabels} />
|
||||
<VariableLabelPickerPlugin variables={variableLabels} isFocus={focus} />
|
||||
<OnBlurPlugin onBlur={onBlur} />
|
||||
<SingleLinePlugin />
|
||||
</LexicalComposer>
|
||||
{focus && hasDropDownPlugin && (
|
||||
<DropDownMenu variables={dropdownVariables} setDropdownValue={setDropdownValue} />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,58 +1,61 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { $getRoot, EditorState, type LexicalEditor } from 'lexical';
|
||||
import React from 'react';
|
||||
import { EditorState, type LexicalEditor } from 'lexical';
|
||||
import { useCallback } from 'react';
|
||||
import { editorStateToText } from '../../Textarea/PromptEditor/utils';
|
||||
import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type';
|
||||
import {
|
||||
EditorVariableLabelPickerType,
|
||||
EditorVariablePickerType
|
||||
} from '../../Textarea/PromptEditor/type';
|
||||
import Editor from './Editor';
|
||||
|
||||
const HttpInput = ({
|
||||
hasVariablePlugin = true,
|
||||
hasDropDownPlugin = false,
|
||||
variables = [],
|
||||
variableLabels = [],
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
h,
|
||||
placeholder,
|
||||
setDropdownValue,
|
||||
updateTrigger
|
||||
}: {
|
||||
hasVariablePlugin?: boolean;
|
||||
hasDropDownPlugin?: boolean;
|
||||
variables?: EditorVariablePickerType[];
|
||||
variableLabels?: EditorVariableLabelPickerType[];
|
||||
value?: string;
|
||||
onChange?: (text: string) => void;
|
||||
onBlur?: (text: string) => void;
|
||||
h?: number;
|
||||
placeholder?: string;
|
||||
setDropdownValue?: (value: string) => void;
|
||||
updateTrigger?: boolean;
|
||||
}) => {
|
||||
const [currentValue, setCurrentValue] = React.useState(value);
|
||||
|
||||
const onChangeInput = useCallback((editorState: EditorState, editor: LexicalEditor) => {
|
||||
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
|
||||
setCurrentValue(text);
|
||||
onChange?.(text);
|
||||
}, []);
|
||||
const onBlurInput = useCallback((editor: LexicalEditor) => {
|
||||
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
|
||||
onBlur?.(text);
|
||||
}, []);
|
||||
const onChangeInput = useCallback(
|
||||
(editorState: EditorState, editor: LexicalEditor) => {
|
||||
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
|
||||
setCurrentValue(text);
|
||||
onChange?.(text);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
const onBlurInput = useCallback(
|
||||
(editor: LexicalEditor) => {
|
||||
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
|
||||
onBlur?.(text);
|
||||
},
|
||||
[onBlur]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor
|
||||
hasVariablePlugin={hasVariablePlugin}
|
||||
hasDropDownPlugin={hasDropDownPlugin}
|
||||
variables={variables}
|
||||
variableLabels={variableLabels}
|
||||
h={h}
|
||||
value={value}
|
||||
currentValue={currentValue}
|
||||
onChange={onChangeInput}
|
||||
onBlur={onBlurInput}
|
||||
placeholder={placeholder}
|
||||
setDropdownValue={setDropdownValue}
|
||||
updateTrigger={updateTrigger}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -23,6 +23,7 @@ export type Props = {
|
||||
trigger?: 'hover' | 'click';
|
||||
iconSize?: string;
|
||||
iconRadius?: string;
|
||||
|
||||
placement?: PlacementWithLogical;
|
||||
menuList: {
|
||||
label?: string;
|
||||
@@ -32,7 +33,8 @@ export type Props = {
|
||||
icon?: IconNameType | string;
|
||||
label: string | React.ReactNode;
|
||||
description?: string;
|
||||
onClick: () => any;
|
||||
onClick?: () => any;
|
||||
menuItemStyles?: MenuItemProps;
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
@@ -75,14 +77,6 @@ const MyMenu = ({
|
||||
}
|
||||
}
|
||||
};
|
||||
const menuItemStyles: MenuItemProps = {
|
||||
borderRadius: 'sm',
|
||||
py: 2,
|
||||
px: 3,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: 'sm'
|
||||
};
|
||||
|
||||
const { isPc } = useSystem();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@@ -166,16 +160,23 @@ const MyMenu = ({
|
||||
{item.children.map((child, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
{...menuItemStyles}
|
||||
onClickCapture={(e) => {
|
||||
borderRadius={'sm'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
child.onClick && child.onClick();
|
||||
if (child.onClick) {
|
||||
setIsOpen(false);
|
||||
child.onClick();
|
||||
}
|
||||
}}
|
||||
py={2}
|
||||
px={3}
|
||||
alignItems={'center'}
|
||||
fontSize={'sm'}
|
||||
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
_notLast={{ mb: 0.5 }}
|
||||
{...typeMapStyle[child.type || 'primary']}
|
||||
{...child.menuItemStyles}
|
||||
>
|
||||
{!!child.icon && (
|
||||
<Avatar
|
||||
@@ -185,12 +186,16 @@ const MyMenu = ({
|
||||
mr={3}
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<Box color={child.description ? 'myGray.900' : 'inherit'} fontSize={'sm'}>
|
||||
<Box w={'100%'}>
|
||||
<Box
|
||||
w={'100%'}
|
||||
color={child.description ? 'myGray.900' : 'inherit'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
{child.label}
|
||||
</Box>
|
||||
{child.description && (
|
||||
<Box color={'myGray.500'} fontSize={'mini'}>
|
||||
<Box color={'myGray.500'} fontSize={'mini'} w={'100%'}>
|
||||
{child.description}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface MyModalProps extends ModalContentProps {
|
||||
isOpen?: boolean;
|
||||
onClose?: () => void;
|
||||
closeOnOverlayClick?: boolean;
|
||||
size?: 'md' | 'lg';
|
||||
}
|
||||
|
||||
const MyModal = ({
|
||||
@@ -35,6 +36,7 @@ const MyModal = ({
|
||||
maxW = ['90vw', '600px'],
|
||||
closeOnOverlayClick = true,
|
||||
iconColor,
|
||||
size = 'md',
|
||||
...props
|
||||
}: MyModalProps) => {
|
||||
const { isPc } = useSystem();
|
||||
@@ -43,6 +45,7 @@ const MyModal = ({
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={() => onClose && onClose()}
|
||||
size={size}
|
||||
autoFocus={false}
|
||||
isCentered={isPc ? isCentered : true}
|
||||
blockScrollOnMount={false}
|
||||
|
||||
@@ -105,7 +105,7 @@ const MultipleRowSelect = ({
|
||||
justifyContent={'space-between'}
|
||||
width={'100%'}
|
||||
rightIcon={<ChevronDownIcon />}
|
||||
variant={'whiteFlow'}
|
||||
variant={'whiteBase'}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
}}
|
||||
|
||||
@@ -10,6 +10,7 @@ type Props<ValueType = string> = Omit<GridProps, 'onChange'> & {
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
inlineStyles?: FlexProps;
|
||||
activeColor?: string;
|
||||
defaultColor?: string;
|
||||
onChange: (value: ValueType) => void;
|
||||
};
|
||||
|
||||
@@ -18,6 +19,7 @@ const LightRowTabs = <ValueType = string,>({
|
||||
size = 'md',
|
||||
value,
|
||||
activeColor = 'primary.600',
|
||||
defaultColor = 'transparent',
|
||||
onChange,
|
||||
inlineStyles,
|
||||
...props
|
||||
@@ -63,7 +65,8 @@ const LightRowTabs = <ValueType = string,>({
|
||||
py={sizeMap.inlineP}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
borderBottom={'2px solid transparent'}
|
||||
borderBottom={'2px solid'}
|
||||
borderColor={defaultColor}
|
||||
px={3}
|
||||
whiteSpace={'nowrap'}
|
||||
{...(value === item.value
|
||||
|
||||
@@ -106,6 +106,7 @@ export default function Editor({
|
||||
cursor={'text'}
|
||||
color={'myGray.700'}
|
||||
bg={focus ? 'white' : bg}
|
||||
borderRadius={'md'}
|
||||
>
|
||||
<LexicalComposer initialConfig={initialConfig} key={key}>
|
||||
<PlainTextPlugin
|
||||
|
||||
@@ -77,10 +77,7 @@ export default function VariableLabelPickerPlugin({
|
||||
onSelectOption={onSelectOption}
|
||||
triggerFn={checkForTriggerMatch}
|
||||
options={variableFilter(variables, queryString || '')}
|
||||
menuRenderFn={(
|
||||
anchorElementRef,
|
||||
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }
|
||||
) => {
|
||||
menuRenderFn={(anchorElementRef, { selectedIndex, selectOptionAndCleanUp }) => {
|
||||
if (anchorElementRef.current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||