mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-17 16:45:02 +00:00
4.8.6 fix (#1970)
* fix: full text search match query * perf: mongo schema import, Avoid duplicate import * feat: mongo log store * doc * fix: sandbox outputs * perf: desc color * fix: node init * perf code * perf: chat header
This commit is contained in:
@@ -13,9 +13,9 @@ weight: 818
|
|||||||
|
|
||||||
### 2. 修改镜像
|
### 2. 修改镜像
|
||||||
|
|
||||||
- fastgpt 镜像 tag 修改成 v4.8.6-alpha
|
- fastgpt 镜像 tag 修改成 v4.8.6-alpha2
|
||||||
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha
|
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha2
|
||||||
- 商业版镜像 tag 修改成 v4.8.6-alpha
|
- 商业版镜像 tag 修改成 v4.8.6-alpha2
|
||||||
|
|
||||||
### 3. 执行初始化
|
### 3. 执行初始化
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
|
|||||||
4. 新增 - 移动文本加工和自定义反馈到基础节点中
|
4. 新增 - 移动文本加工和自定义反馈到基础节点中
|
||||||
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
|
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
|
||||||
6. 优化 - 知识库导入接口,返回值对齐
|
6. 优化 - 知识库导入接口,返回值对齐
|
||||||
7. 修复 - 工作流中团队插件加载异常
|
7. 优化 - Mongo model 重复加载
|
||||||
8. 修复 - 知识库集合目录导航失效
|
8. 修复 - 工作流中团队插件加载异常
|
||||||
9. 修复 - 通过 API 调用 chat 接口,传递 System 异常
|
9. 修复 - 知识库集合目录导航失效
|
||||||
|
10. 修复 - 通过 API 调用 chat 接口,传递 System 异常
|
@@ -8,5 +8,10 @@ export const Output_Template_AddOutput: FlowNodeOutputItemType = {
|
|||||||
key: NodeOutputKeyEnum.addOutputParam,
|
key: NodeOutputKeyEnum.addOutputParam,
|
||||||
type: FlowNodeOutputTypeEnum.dynamic,
|
type: FlowNodeOutputTypeEnum.dynamic,
|
||||||
valueType: WorkflowIOValueTypeEnum.dynamic,
|
valueType: WorkflowIOValueTypeEnum.dynamic,
|
||||||
label: ''
|
label: '',
|
||||||
|
customFieldConfig: {
|
||||||
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
|
showDescription: false,
|
||||||
|
showDefaultValue: false
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
...Output_Template_AddOutput,
|
...Output_Template_AddOutput
|
||||||
customFieldConfig: {
|
|
||||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
|
||||||
showDescription: false,
|
|
||||||
showDefaultValue: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.error,
|
id: NodeOutputKeyEnum.error,
|
||||||
|
@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
|
|||||||
showDefaultValue: true
|
showDefaultValue: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||||
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
canEdit: true,
|
||||||
|
key: 'data1',
|
||||||
|
label: 'data1',
|
||||||
|
customInputConfig: {
|
||||||
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
|
showDescription: false,
|
||||||
|
showDefaultValue: true
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||||
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
canEdit: true,
|
||||||
|
key: 'data2',
|
||||||
|
label: 'data2',
|
||||||
|
customInputConfig: {
|
||||||
|
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||||
|
showDescription: false,
|
||||||
|
showDefaultValue: true
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: NodeInputKeyEnum.codeType,
|
key: NodeInputKeyEnum.codeType,
|
||||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||||
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
|
|||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
...Output_Template_AddOutput,
|
...Output_Template_AddOutput,
|
||||||
description: '将代码中 return 的对象作为输出,传递给后续的节点'
|
description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: NodeOutputKeyEnum.rawResponse,
|
id: NodeOutputKeyEnum.rawResponse,
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import { addLog } from '../../common/system/log';
|
import { addLog } from '../../common/system/log';
|
||||||
import mongoose, { Model } from 'mongoose';
|
import mongoose, { Model } from 'mongoose';
|
||||||
|
|
||||||
|
export default mongoose;
|
||||||
|
export * from 'mongoose';
|
||||||
|
|
||||||
export const connectionMongo = (() => {
|
export const connectionMongo = (() => {
|
||||||
if (!global.mongodb) {
|
if (!global.mongodb) {
|
||||||
global.mongodb = mongoose;
|
global.mongodb = mongoose;
|
||||||
@@ -9,9 +12,6 @@ export const connectionMongo = (() => {
|
|||||||
return global.mongodb;
|
return global.mongodb;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export default mongoose;
|
|
||||||
export * from 'mongoose';
|
|
||||||
|
|
||||||
const addCommonMiddleware = (schema: mongoose.Schema) => {
|
const addCommonMiddleware = (schema: mongoose.Schema) => {
|
||||||
const operations = [
|
const operations = [
|
||||||
/^find/,
|
/^find/,
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { LogLevelEnum } from './log/constant';
|
import { LogLevelEnum } from './log/constant';
|
||||||
// import { MongoLog } from './log/schema';
|
import { connectionMongo } from '../mongo/index';
|
||||||
import connectionMongo from '../mongo/index';
|
import { getMongoLog } from './log/schema';
|
||||||
|
|
||||||
const logMap = {
|
const logMap = {
|
||||||
[LogLevelEnum.debug]: {
|
[LogLevelEnum.debug]: {
|
||||||
@@ -52,14 +52,14 @@ export const addLog = {
|
|||||||
level === LogLevelEnum.error && console.error(obj);
|
level === LogLevelEnum.error && console.error(obj);
|
||||||
|
|
||||||
// store
|
// store
|
||||||
// if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
|
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
|
||||||
// // store log
|
// store log
|
||||||
// MongoLog.create({
|
getMongoLog().create({
|
||||||
// text: msg,
|
text: msg,
|
||||||
// level,
|
level,
|
||||||
// metadata: obj
|
metadata: obj
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
},
|
},
|
||||||
debug(msg: string, obj?: Record<string, any>) {
|
debug(msg: string, obj?: Record<string, any>) {
|
||||||
this.log(LogLevelEnum.debug, msg, obj);
|
this.log(LogLevelEnum.debug, msg, obj);
|
||||||
|
@@ -4,24 +4,26 @@ import { LogLevelEnum } from './constant';
|
|||||||
|
|
||||||
export const LogCollectionName = 'system_logs';
|
export const LogCollectionName = 'system_logs';
|
||||||
|
|
||||||
const SystemLogSchema = new Schema({
|
export const getMongoLog = () => {
|
||||||
text: {
|
const SystemLogSchema = new Schema({
|
||||||
type: String,
|
text: {
|
||||||
required: true
|
type: String,
|
||||||
},
|
required: true
|
||||||
level: {
|
},
|
||||||
type: String,
|
level: {
|
||||||
required: true,
|
type: String,
|
||||||
enum: Object.values(LogLevelEnum)
|
required: true,
|
||||||
},
|
enum: Object.values(LogLevelEnum)
|
||||||
time: {
|
},
|
||||||
type: Date,
|
time: {
|
||||||
default: () => new Date()
|
type: Date,
|
||||||
},
|
default: () => new Date()
|
||||||
metadata: Object
|
},
|
||||||
});
|
metadata: Object
|
||||||
|
});
|
||||||
|
|
||||||
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
|
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
|
||||||
SystemLogSchema.index({ level: 1 });
|
SystemLogSchema.index({ level: 1 });
|
||||||
|
|
||||||
export const MongoLog = getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
|
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
|
||||||
|
};
|
||||||
|
@@ -57,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
"module": {
|
"module": {
|
||||||
"Combine Modules": "Combine Modules",
|
"Combine Modules": "Combine Modules",
|
||||||
"Confirm Sync": "Using the latest template will overwrite the existing one and may result in the loss of some previous configuration information. Please confirm.",
|
"Confirm Sync": "The template will be updated to the latest template configuration. Fields that do not exist in the template will be deleted (including all custom fields). You are advised to make a copy of the node and then update the original node version.",
|
||||||
"Custom Title Tip": "This title will be displayed during the conversation",
|
"Custom Title Tip": "This title will be displayed during the conversation",
|
||||||
"My Modules": "My Modules",
|
"My Modules": "My Modules",
|
||||||
"No Modules": "No plugins yet~",
|
"No Modules": "No plugins yet~",
|
||||||
|
@@ -1068,7 +1068,7 @@
|
|||||||
"Debug": "Debug",
|
"Debug": "Debug",
|
||||||
"Debug Node": "Debug mode",
|
"Debug Node": "Debug mode",
|
||||||
"Failed": "Execution failed",
|
"Failed": "Execution failed",
|
||||||
"Not intro": "This node has no introduction~\\",
|
"Not intro": "This node has no introduction~",
|
||||||
"Run from here": "Run from here",
|
"Run from here": "Run from here",
|
||||||
"Run result": "Run result",
|
"Run result": "Run result",
|
||||||
"Running": "Running",
|
"Running": "Running",
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
"Tool input": "Tool",
|
"Tool input": "Tool",
|
||||||
"code": {
|
"code": {
|
||||||
"Reset template": "Reset template",
|
"Reset template": "Reset template",
|
||||||
"Reset template confirm": "Are you sure to restore the code template? Be careful to save the current code."
|
"Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code."
|
||||||
},
|
},
|
||||||
"ifelse": {
|
"ifelse": {
|
||||||
"Input value": "Input",
|
"Input value": "Input",
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
},
|
},
|
||||||
"module": {
|
"module": {
|
||||||
"Combine Modules": "组合模块",
|
"Combine Modules": "组合模块",
|
||||||
"Confirm Sync": "将会使用最新模板进行覆盖,可能会丢失一些旧的配置信息,请确认",
|
"Confirm Sync": "将会更新至最新的模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
|
||||||
"Custom Title Tip": "该标题名字会展示在对话过程中",
|
"Custom Title Tip": "该标题名字会展示在对话过程中",
|
||||||
"My Modules": "",
|
"My Modules": "",
|
||||||
"No Modules": "没找到插件",
|
"No Modules": "没找到插件",
|
||||||
|
@@ -1077,7 +1077,7 @@
|
|||||||
"Debug": "调试",
|
"Debug": "调试",
|
||||||
"Debug Node": "Debug 模式",
|
"Debug Node": "Debug 模式",
|
||||||
"Failed": "运行失败",
|
"Failed": "运行失败",
|
||||||
"Not intro": "这个节点没有介绍~\\",
|
"Not intro": "这个节点没有介绍~",
|
||||||
"Run from here": "从这里开始运行",
|
"Run from here": "从这里开始运行",
|
||||||
"Run result": "",
|
"Run result": "",
|
||||||
"Running": "运行中",
|
"Running": "运行中",
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
"Tool input": "工具参数",
|
"Tool input": "工具参数",
|
||||||
"code": {
|
"code": {
|
||||||
"Reset template": "还原模板",
|
"Reset template": "还原模板",
|
||||||
"Reset template confirm": "确认还原代码模板?请注意保存当前代码。"
|
"Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
|
||||||
},
|
},
|
||||||
"ifelse": {
|
"ifelse": {
|
||||||
"Input value": "输入值",
|
"Input value": "输入值",
|
||||||
|
@@ -155,7 +155,7 @@ const SelectOneResource = ({
|
|||||||
return loading ? (
|
return loading ? (
|
||||||
<Loading fixed={false} />
|
<Loading fixed={false} />
|
||||||
) : (
|
) : (
|
||||||
<Box maxH={maxH} overflow={'auto'}>
|
<Box maxH={maxH} h={'100%'} overflow={'auto'}>
|
||||||
<Render list={concatRoot} />
|
<Render list={concatRoot} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@@ -63,8 +63,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
const avatar = getValues('avatar');
|
const avatar = getValues('avatar');
|
||||||
|
|
||||||
// submit config
|
// submit config
|
||||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
const { runAsync: saveSubmitSuccess, loading: btnLoading } = useRequest2(
|
||||||
mutationFn: async (data: AppSchema) => {
|
async (data: AppSchema) => {
|
||||||
await updateAppDetail({
|
await updateAppDetail({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
avatar: data.avatar,
|
avatar: data.avatar,
|
||||||
@@ -72,16 +72,17 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
defaultPermission: data.defaultPermission
|
defaultPermission: data.defaultPermission
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess() {
|
{
|
||||||
onClose();
|
onSuccess() {
|
||||||
toast({
|
toast({
|
||||||
title: t('common.Update Success'),
|
title: t('common.Update Success'),
|
||||||
status: 'success'
|
status: 'success'
|
||||||
});
|
});
|
||||||
reloadApp();
|
reloadApp();
|
||||||
},
|
},
|
||||||
errorToast: t('common.Update Failed')
|
errorToast: t('common.Update Failed')
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const saveSubmitError = useCallback(() => {
|
const saveSubmitError = useCallback(() => {
|
||||||
// deep search message
|
// deep search message
|
||||||
@@ -101,8 +102,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
}, [errors, t, toast]);
|
}, [errors, t, toast]);
|
||||||
|
|
||||||
const saveUpdateModel = useCallback(
|
const saveUpdateModel = useCallback(
|
||||||
() => handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(),
|
() => handleSubmit((data) => saveSubmitSuccess(data).then(onClose), saveSubmitError)(),
|
||||||
[handleSubmit, saveSubmitError, saveSubmitSuccess]
|
[handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
const onSelectFile = useCallback(
|
||||||
@@ -210,7 +211,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
isInheritPermission={appDetail.inheritPermission}
|
isInheritPermission={appDetail.inheritPermission}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
setValue('defaultPermission', v);
|
setValue('defaultPermission', v);
|
||||||
handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
|
return handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
|
||||||
}}
|
}}
|
||||||
hasParent={!!appDetail.parentId}
|
hasParent={!!appDetail.parentId}
|
||||||
/>
|
/>
|
||||||
|
@@ -396,7 +396,9 @@ const RenderList = React.memo(function RenderList({
|
|||||||
{t(template.name)}
|
{t(template.name)}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box mt={2}>{t(template.intro || 'core.workflow.Not intro')}</Box>
|
<Box mt={2} color={'myGray.500'}>
|
||||||
|
{t(template.intro) || t('core.workflow.Not intro')}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@@ -16,7 +16,8 @@ import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants';
|
import { getLatestNodeTemplate } from '@/web/core/workflow/utils';
|
||||||
|
import { CodeNode } from '@fastgpt/global/core/workflow/template/system/sandbox';
|
||||||
|
|
||||||
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -24,6 +25,8 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
const { nodeId, inputs, outputs } = data;
|
const { nodeId, inputs, outputs } = data;
|
||||||
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
|
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
|
||||||
|
|
||||||
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
|
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
|
||||||
const { ConfirmModal, openConfirm } = useConfirm({
|
const { ConfirmModal, openConfirm } = useConfirm({
|
||||||
content: workflowT('code.Reset template confirm')
|
content: workflowT('code.Reset template confirm')
|
||||||
@@ -41,14 +44,9 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
color={'primary.500'}
|
color={'primary.500'}
|
||||||
fontSize={'xs'}
|
fontSize={'xs'}
|
||||||
onClick={openConfirm(() => {
|
onClick={openConfirm(() => {
|
||||||
onChangeNode({
|
onResetNode({
|
||||||
nodeId,
|
id: nodeId,
|
||||||
type: 'updateInput',
|
node: getLatestNodeTemplate(data, CodeNode)
|
||||||
key: item.key,
|
|
||||||
value: {
|
|
||||||
...item,
|
|
||||||
value: JS_TEMPLATE
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@@ -37,6 +37,7 @@ export const defaultInput: FlowNodeInputItemType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
|
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
|
||||||
selectedTypeIndex: 0,
|
selectedTypeIndex: 0,
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
canEdit: true,
|
||||||
key: '',
|
key: '',
|
||||||
label: ''
|
label: ''
|
||||||
};
|
};
|
||||||
|
@@ -16,7 +16,7 @@ import { useDebug } from '../../hooks/useDebug';
|
|||||||
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
|
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
|
||||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||||
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
|
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
|
||||||
import { storeNode2FlowNode, updateFlowNodeVersion } from '@/web/core/workflow/utils';
|
import { storeNode2FlowNode, getLatestNodeTemplate } from '@/web/core/workflow/utils';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../../context';
|
import { WorkflowContext } from '../../../context';
|
||||||
@@ -24,8 +24,6 @@ import { useI18n } from '@/web/context/I18n';
|
|||||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|
||||||
import { useMount } from 'ahooks';
|
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useWorkflowUtils } from '../../hooks/useUtils';
|
import { useWorkflowUtils } from '../../hooks/useUtils';
|
||||||
|
|
||||||
@@ -56,13 +54,11 @@ const NodeCard = (props: Props) => {
|
|||||||
minW = '300px',
|
minW = '300px',
|
||||||
maxW = '600px',
|
maxW = '600px',
|
||||||
nodeId,
|
nodeId,
|
||||||
flowNodeType,
|
|
||||||
selected,
|
selected,
|
||||||
menuForbid,
|
menuForbid,
|
||||||
isTool = false,
|
isTool = false,
|
||||||
isError = false,
|
isError = false,
|
||||||
debugResult,
|
debugResult
|
||||||
pluginId
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
@@ -106,11 +102,11 @@ const NodeCard = (props: Props) => {
|
|||||||
);
|
);
|
||||||
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
|
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
|
||||||
|
|
||||||
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
|
const { runAsync: onClickSyncVersion } = useRequest2(
|
||||||
|
async () => {
|
||||||
const onClickSyncVersion = useCallback(async () => {
|
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
|
||||||
try {
|
|
||||||
if (!node || !template) return;
|
if (!node || !template) return;
|
||||||
|
|
||||||
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
|
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
|
||||||
if (!node.pluginId) return;
|
if (!node.pluginId) return;
|
||||||
onResetNode({
|
onResetNode({
|
||||||
@@ -120,14 +116,15 @@ const NodeCard = (props: Props) => {
|
|||||||
} else {
|
} else {
|
||||||
onResetNode({
|
onResetNode({
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
node: updateFlowNodeVersion(node, template)
|
node: getLatestNodeTemplate(node, template)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await getNodeVersion();
|
await getNodeVersion();
|
||||||
} catch (error) {
|
},
|
||||||
console.error('Error fetching plugin module:', error);
|
{
|
||||||
|
refreshDeps: [node, nodeId, onResetNode, getNodeVersion]
|
||||||
}
|
}
|
||||||
}, [getNodeVersion, node, nodeId, onResetNode, template]);
|
);
|
||||||
|
|
||||||
/* Node header */
|
/* Node header */
|
||||||
const Header = useMemo(() => {
|
const Header = useMemo(() => {
|
||||||
@@ -197,12 +194,7 @@ const NodeCard = (props: Props) => {
|
|||||||
</MyTooltip>
|
</MyTooltip>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<MenuRender
|
<MenuRender nodeId={nodeId} menuForbid={menuForbid} />
|
||||||
nodeId={nodeId}
|
|
||||||
pluginId={pluginId}
|
|
||||||
flowNodeType={flowNodeType}
|
|
||||||
menuForbid={menuForbid}
|
|
||||||
/>
|
|
||||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||||
</Box>
|
</Box>
|
||||||
<ConfirmSyncModal />
|
<ConfirmSyncModal />
|
||||||
@@ -219,8 +211,6 @@ const NodeCard = (props: Props) => {
|
|||||||
appT,
|
appT,
|
||||||
onOpenConfirmSync,
|
onOpenConfirmSync,
|
||||||
onClickSyncVersion,
|
onClickSyncVersion,
|
||||||
pluginId,
|
|
||||||
flowNodeType,
|
|
||||||
intro,
|
intro,
|
||||||
ConfirmSyncModal,
|
ConfirmSyncModal,
|
||||||
onOpenCustomTitleModal,
|
onOpenCustomTitleModal,
|
||||||
@@ -274,13 +264,9 @@ export default React.memo(NodeCard);
|
|||||||
|
|
||||||
const MenuRender = React.memo(function MenuRender({
|
const MenuRender = React.memo(function MenuRender({
|
||||||
nodeId,
|
nodeId,
|
||||||
pluginId,
|
|
||||||
flowNodeType,
|
|
||||||
menuForbid
|
menuForbid
|
||||||
}: {
|
}: {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
pluginId?: string;
|
|
||||||
flowNodeType: Props['flowNodeType'];
|
|
||||||
menuForbid?: Props['menuForbid'];
|
menuForbid?: Props['menuForbid'];
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@@ -164,8 +164,12 @@ const ChatHistorySlider = ({
|
|||||||
px: 1
|
px: 1
|
||||||
}}
|
}}
|
||||||
list={[
|
list={[
|
||||||
{ label: t('core.chat.Recent use'), value: TabEnum.recently },
|
...(isTeamChat
|
||||||
...(!isTeamChat ? [{ label: t('App'), value: TabEnum.app }] : []),
|
? [{ label: t('App'), value: TabEnum.recently }]
|
||||||
|
: [
|
||||||
|
{ label: t('core.chat.Recent use'), value: TabEnum.recently },
|
||||||
|
{ label: t('App'), value: TabEnum.app }
|
||||||
|
]),
|
||||||
{ label: t('core.chat.History'), value: TabEnum.history }
|
{ label: t('core.chat.History'), value: TabEnum.history }
|
||||||
]}
|
]}
|
||||||
value={currentTab}
|
value={currentTab}
|
||||||
@@ -185,8 +189,8 @@ const ChatHistorySlider = ({
|
|||||||
>
|
>
|
||||||
{t('core.chat.New Chat')}
|
{t('core.chat.New Chat')}
|
||||||
</Button>
|
</Button>
|
||||||
|
{/* Clear */}
|
||||||
{(isPc || !showApps) && (
|
{isPc && (
|
||||||
<IconButton
|
<IconButton
|
||||||
ml={3}
|
ml={3}
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
|
@@ -8,6 +8,7 @@ import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/cons
|
|||||||
import {
|
import {
|
||||||
EDGE_TYPE,
|
EDGE_TYPE,
|
||||||
FlowNodeInputTypeEnum,
|
FlowNodeInputTypeEnum,
|
||||||
|
FlowNodeOutputTypeEnum,
|
||||||
FlowNodeTypeEnum,
|
FlowNodeTypeEnum,
|
||||||
defaultNodeVersion
|
defaultNodeVersion
|
||||||
} from '@fastgpt/global/core/workflow/node/constant';
|
} from '@fastgpt/global/core/workflow/node/constant';
|
||||||
@@ -34,6 +35,7 @@ import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/syste
|
|||||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||||
import { cloneDeep, isEqual } from 'lodash';
|
import { cloneDeep, isEqual } from 'lodash';
|
||||||
|
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
|
||||||
|
|
||||||
export const nodeTemplate2FlowNode = ({
|
export const nodeTemplate2FlowNode = ({
|
||||||
template,
|
template,
|
||||||
@@ -70,13 +72,24 @@ export const storeNode2FlowNode = ({
|
|||||||
moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) ||
|
moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) ||
|
||||||
EmptyNode;
|
EmptyNode;
|
||||||
|
|
||||||
|
const templateInputs = template.inputs.filter((input) => !input.canEdit);
|
||||||
|
const templateOutputs = template.outputs.filter(
|
||||||
|
(output) => output.type !== FlowNodeOutputTypeEnum.dynamic
|
||||||
|
);
|
||||||
|
const dynamicInput = template.inputs.find(
|
||||||
|
(input) => input.renderTypeList[0] === FlowNodeInputTypeEnum.addInputParam
|
||||||
|
);
|
||||||
|
|
||||||
// replace item data
|
// replace item data
|
||||||
const nodeItem: FlowNodeItemType = {
|
const nodeItem: FlowNodeItemType = {
|
||||||
...template,
|
...template,
|
||||||
...storeNode,
|
...storeNode,
|
||||||
version: storeNode.version ?? template.version ?? defaultNodeVersion,
|
version: storeNode.version ?? template.version ?? defaultNodeVersion,
|
||||||
|
|
||||||
inputs: template.inputs
|
/*
|
||||||
|
Inputs and outputs, New fields are added, not reduced
|
||||||
|
*/
|
||||||
|
inputs: templateInputs
|
||||||
.map<FlowNodeInputItemType>((templateInput) => {
|
.map<FlowNodeInputItemType>((templateInput) => {
|
||||||
const storeInput =
|
const storeInput =
|
||||||
storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput;
|
storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput;
|
||||||
@@ -91,27 +104,35 @@ export const storeNode2FlowNode = ({
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.concat(
|
.concat(
|
||||||
/*
|
/* Concat dynamic inputs */
|
||||||
1. Plugin input
|
storeNode.inputs
|
||||||
2. Old version adapt: Dynamic input will be added to the node inputs.
|
.filter((item) => !templateInputs.find((input) => input.key === item.key))
|
||||||
*/
|
.map((item) => {
|
||||||
storeNode.inputs.filter((item) => !template.inputs.find((input) => input.key === item.key))
|
if (!dynamicInput) return item;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
...getInputComponentProps(dynamicInput)
|
||||||
|
};
|
||||||
|
})
|
||||||
),
|
),
|
||||||
outputs: template.outputs
|
outputs: templateOutputs
|
||||||
.map<FlowNodeOutputItemType>((templateOutput) => {
|
.map<FlowNodeOutputItemType>((templateOutput) => {
|
||||||
const storeOutput =
|
const storeOutput =
|
||||||
template.outputs.find((item) => item.key === templateOutput.key) || templateOutput;
|
template.outputs.find((item) => item.key === templateOutput.key) || templateOutput;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...storeOutput,
|
...storeOutput,
|
||||||
...templateOutput,
|
...templateOutput,
|
||||||
|
|
||||||
id: storeOutput.id ?? templateOutput.id,
|
id: storeOutput.id ?? templateOutput.id,
|
||||||
|
label: storeOutput.label ?? templateOutput.label,
|
||||||
value: storeOutput.value ?? templateOutput.value
|
value: storeOutput.value ?? templateOutput.value
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.concat(
|
.concat(
|
||||||
storeNode.outputs.filter(
|
storeNode.outputs.filter(
|
||||||
(item) => !template.outputs.find((output) => output.key === item.key)
|
(item) => !templateOutputs.find((output) => output.key === item.key)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@@ -365,37 +386,42 @@ export const getWorkflowGlobalVariables = ({
|
|||||||
|
|
||||||
export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>;
|
export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>;
|
||||||
|
|
||||||
export const updateFlowNodeVersion = (
|
/* Reset node to latest version */
|
||||||
|
export const getLatestNodeTemplate = (
|
||||||
node: FlowNodeItemType,
|
node: FlowNodeItemType,
|
||||||
template: FlowNodeTemplateType
|
template: FlowNodeTemplateType
|
||||||
): FlowNodeItemType => {
|
): FlowNodeItemType => {
|
||||||
function updateArrayBasedOnTemplate<T extends FlowNodeInputItemType | FlowNodeOutputItemType>(
|
|
||||||
nodeArray: T[],
|
|
||||||
templateArray: T[]
|
|
||||||
): T[] {
|
|
||||||
return templateArray.map((templateItem) => {
|
|
||||||
const nodeItem = nodeArray.find((item) => item.key === templateItem.key);
|
|
||||||
if (nodeItem) {
|
|
||||||
return { ...templateItem, ...nodeItem } as T;
|
|
||||||
}
|
|
||||||
return { ...templateItem };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedNode: FlowNodeItemType = {
|
const updatedNode: FlowNodeItemType = {
|
||||||
...node,
|
...node,
|
||||||
...template,
|
...template,
|
||||||
|
inputs: template.inputs.map((templateItem) => {
|
||||||
|
const nodeItem = node.inputs.find((item) => item.key === templateItem.key);
|
||||||
|
if (nodeItem) {
|
||||||
|
return {
|
||||||
|
...templateItem,
|
||||||
|
value: nodeItem.value,
|
||||||
|
selectedTypeIndex: nodeItem.selectedTypeIndex,
|
||||||
|
valueType: nodeItem.valueType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...templateItem };
|
||||||
|
}),
|
||||||
|
outputs: template.outputs.map((templateItem) => {
|
||||||
|
const nodeItem = node.outputs.find((item) => item.key === templateItem.key);
|
||||||
|
if (nodeItem) {
|
||||||
|
return {
|
||||||
|
...templateItem,
|
||||||
|
id: nodeItem.id,
|
||||||
|
value: nodeItem.value,
|
||||||
|
valueType: nodeItem.valueType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...templateItem };
|
||||||
|
}),
|
||||||
name: node.name,
|
name: node.name,
|
||||||
intro: node.intro
|
intro: node.intro
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node.inputs && template.inputs) {
|
|
||||||
updatedNode.inputs = updateArrayBasedOnTemplate(node.inputs, template.inputs);
|
|
||||||
}
|
|
||||||
if (node.outputs && template.outputs) {
|
|
||||||
updatedNode.outputs = updateArrayBasedOnTemplate(node.outputs, template.outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedNode;
|
return updatedNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user