Perf workflow (#1492)

* perf: handle edge check

* search model

* feat: plugin input can render all input; fix: plugin default value

* fix ts

* feat: plugin input support required
This commit is contained in:
Archer
2024-05-15 16:17:43 +08:00
committed by GitHub
parent cd876251b7
commit 8386f707cd
36 changed files with 256 additions and 160 deletions

View File

@@ -96,7 +96,7 @@ export const DatasetSelectModal = ({
_hover={{ color: 'red.500' }}
onClick={() => {
setSelectedDatasets((state) =>
state.filter((kb) => kb.datasetId !== item._id)
state.filter((dataset) => dataset.datasetId !== item._id)
);
}}
/>
@@ -141,7 +141,9 @@ export const DatasetSelectModal = ({
if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id);
} else {
const vectorModel = selectedDatasets[0]?.vectorModel?.model;
const vectorModel = datasets.find(
(dataset) => dataset._id === selectedDatasets[0]?.datasetId
)?.vectorModel?.model;
if (vectorModel && vectorModel !== item.vectorModel.model) {
return toast({
@@ -149,10 +151,7 @@ export const DatasetSelectModal = ({
title: t('dataset.Select Dataset Tips')
});
}
setSelectedDatasets((state) => [
...state,
{ datasetId: item._id, vectorModel: item.vectorModel }
]);
setSelectedDatasets((state) => [...state, { datasetId: item._id }]);
}
}}
>

View File

@@ -26,6 +26,8 @@ import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants
import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '../../context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
const MyRightDrawer = dynamic(
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
@@ -109,15 +111,23 @@ export const useDebug = () => {
const runtimeNode = runtimeNodes.find((node) => node.nodeId === runtimeNodeId);
if (!runtimeNode) return <></>;
const referenceInputs = runtimeNode.inputs.filter((input) => {
const renderInputs = runtimeNode.inputs.filter((input) => {
if (runtimeNode.flowNodeType === FlowNodeTypeEnum.pluginInput) return true;
if (checkInputIsReference(input)) return true;
if (input.required && !input.value) return true;
});
const { register, getValues, setValue, handleSubmit } = useForm<Record<string, any>>({
defaultValues: referenceInputs.reduce((acc, input) => {
//@ts-ignore
acc[input.key] = undefined;
defaultValues: renderInputs.reduce((acc: Record<string, any>, input) => {
const isReference = checkInputIsReference(input);
if (isReference) {
acc[input.key] = undefined;
} else if (typeof input.value === 'object') {
acc[input.key] = JSON.stringify(input.value, null, 2);
} else {
acc[input.key] = input.value;
}
return acc;
}, {})
});
@@ -153,73 +163,81 @@ export const useDebug = () => {
iconSrc="core/workflow/debugBlue"
title={t('core.workflow.Debug Node')}
maxW={['90vw', '35vw']}
px={0}
>
<Flex flexDirection={'column'} h={'100%'}>
<Box flex={'1 0 0'} overflow={'auto'}>
{referenceInputs.map((input) => {
<Box flex={'1 0 0'} overflow={'auto'} px={6}>
{renderInputs.map((input) => {
const required = input.required || false;
return (
<Box key={input.key} _notLast={{ mb: 4 }} px={1}>
<Box display={'inline-block'} position={'relative'} mb={1}>
{required && (
<Box position={'absolute'} right={-2} top={-1} color={'red.600'}>
*
</Box>
)}
{t(input.debugLabel || input.label)}
</Box>
{(() => {
if (input.valueType === WorkflowIOValueTypeEnum.string) {
return (
<Textarea
{...register(input.key, {
required
})}
placeholder={t(input.placeholder || '')}
bg={'myGray.50'}
/>
);
}
if (input.valueType === WorkflowIOValueTypeEnum.number) {
return (
<NumberInput
step={input.step}
min={input.min}
max={input.max}
bg={'myGray.50'}
>
<NumberInputField
{...register(input.key, {
required: input.required,
min: input.min,
max: input.max,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
);
}
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
return <Switch size={'lg'} {...register(input.key)} />;
}
return (
<JsonEditor
bg={'myGray.50'}
placeholder={t(input.placeholder || '')}
resize
value={getValues(input.key)}
onChange={(e) => {
setValue(input.key, e);
}}
console.log(input.valueType);
const RenderInput = (() => {
if (input.valueType === WorkflowIOValueTypeEnum.string) {
return (
<Textarea
{...register(input.key, {
required
})}
placeholder={t(input.placeholder || '')}
bg={'myGray.50'}
/>
);
}
if (input.valueType === WorkflowIOValueTypeEnum.number) {
return (
<NumberInput step={input.step} min={input.min} max={input.max} bg={'myGray.50'}>
<NumberInputField
{...register(input.key, {
required: input.required,
min: input.min,
max: input.max,
valueAsNumber: true
})}
/>
);
})()}
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
);
}
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
return (
<Box>
<Switch size={'lg'} {...register(input.key)} />
</Box>
);
}
if (typeof input.value === 'string') {
return (
<JsonEditor
bg={'myGray.50'}
placeholder={t(input.placeholder || '')}
resize
value={getValues(input.key)}
onChange={(e) => {
setValue(input.key, e);
}}
/>
);
}
})();
return !!RenderInput ? (
<Box key={input.key} _notLast={{ mb: 4 }} px={1}>
<Flex alignItems={'center'} mb={1}>
<Box position={'relative'}>
{required && (
<Box position={'absolute'} right={-2} top={'-1px'} color={'red.600'}>
*
</Box>
)}
{t(input.debugLabel || input.label)}
</Box>
{input.description && <QuestionTip ml={2} label={input.description} />}
</Flex>
{RenderInput}
</Box>
);
) : null;
})}
</Box>
<Flex py={2} justifyContent={'flex-end'}>

View File

@@ -35,7 +35,7 @@ import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
[FlowNodeTypeEnum.emptyNode]: NodeSimple,
[FlowNodeTypeEnum.globalVariable]: NodeSimple,
[FlowNodeTypeEnum.systemConfig]: dynamic(() => import('./nodes/NodeSystemConfig')),

View File

@@ -146,17 +146,9 @@ const NodePluginInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
if (!input) return;
setEditField({
...input,
inputType: input.renderTypeList[0],
valueType: input.valueType,
key: input.key,
label: input.label,
description: input.description,
isToolInput: !!input.toolDescription,
defaultValue: input.defaultValue,
maxLength: input.maxLength,
max: input.max,
min: input.min,
dynamicParamDefaultValue: input.dynamicParamDefaultValue
isToolInput: !!input.toolDescription
});
}}
onEdit={({ data, changeKey }) => {
@@ -165,20 +157,14 @@ const NodePluginInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const output = outputs.find((output) => output.key === editField.key);
const newInput: FlowNodeInputItemType = {
...data,
key: data.key,
valueType: data.valueType,
label: data.label || '',
renderTypeList: [data.inputType],
required: data.required,
description: data.description,
toolDescription: data.isToolInput ? data.description : undefined,
canEdit: true,
value: data.defaultValue,
editField: dynamicInputEditField,
maxLength: data.maxLength,
max: data.max,
min: data.min,
dynamicParamDefaultValue: data.dynamicParamDefaultValue
editField: dynamicInputEditField
};
const newOutput: FlowNodeOutputItemType = {
...(output as FlowNodeOutputItemType),

View File

@@ -24,6 +24,8 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import dynamic from 'next/dynamic';
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput/index';
import { useI18n } from '@/web/context/I18n';
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
const EmptyTip = dynamic(() => import('@fastgpt/web/components/common/EmptyTip'));
@@ -63,6 +65,7 @@ const FieldEditModal = ({
onSubmit: (e: { data: EditNodeFieldType; changeKey: boolean }) => void;
}) => {
const { t } = useTranslation();
const { workflowT } = useI18n();
const { toast } = useToast();
const showDynamicInputSelect =
!keys.includes(NodeInputKeyEnum.addInputParam) ||
@@ -274,7 +277,6 @@ const FieldEditModal = ({
);
const onSubmitError = useCallback(
(e: Object) => {
console.log(e);
for (const item of Object.values(e)) {
if (item.message) {
toast({
@@ -293,7 +295,6 @@ const FieldEditModal = ({
isOpen={true}
iconSrc="/imgs/workflow/extract.png"
title={t('core.module.edit.Field Edit')}
onClose={onClose}
maxW={['90vw', showInputTypeSelect ? '800px' : '400px']}
w={'100%'}
overflow={'unset'}
@@ -364,6 +365,10 @@ const FieldEditModal = ({
{/* input type config */}
{showInputTypeSelect && (
<Stack flex={1} gap={5}>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}>{workflowT('Field required')}</Box>
<Switch {...register('required')} />
</Flex>
{showToolInput && (
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}></Box>
@@ -426,10 +431,16 @@ const FieldEditModal = ({
{showMaxLenInput && (
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}>{t('core.module.Max Length')}</Box>
<Input
<MyNumberInput
flex={'1 0 0'}
bg={'myGray.50'}
placeholder={t('core.module.Max Length placeholder')}
{...register('maxLength')}
value={maxLength}
onChange={(e) => {
// @ts-ignore
setValue('maxLength', e);
}}
// {...register('maxLength')}
/>
</Flex>
)}
@@ -437,11 +448,27 @@ const FieldEditModal = ({
<>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}>{t('core.module.Max Value')}</Box>
<Input bg={'myGray.50'} type={'number'} {...register('max')} />
<MyNumberInput
flex={'1 0 0'}
bg={'myGray.50'}
value={watch('max')}
onChange={(e) => {
// @ts-ignore
setValue('max', e);
}}
/>
</Flex>
<Flex alignItems={'center'}>
<Box flex={'0 0 70px'}>{t('core.module.Min Value')}</Box>
<Input bg={'myGray.50'} type={'number'} {...register('min')} />
<MyNumberInput
flex={'1 0 0'}
bg={'myGray.50'}
value={watch('min')}
onChange={(e) => {
// @ts-ignore
setValue('min', e);
}}
/>
</Flex>
</>
)}

View File

@@ -55,7 +55,6 @@ const NodeCard = (props: Props) => {
maxW = '600px',
nodeId,
flowNodeType,
inputs,
selected,
menuForbid,
isTool = false,

View File

@@ -90,17 +90,13 @@ const InputLabel = ({ nodeId, input }: Props) => {
_hover={{ color: 'primary.500' }}
onClick={() =>
setEditField({
...input,
inputType: renderTypeList[0],
valueType: valueType,
key,
label,
description,
isToolInput: !!toolDescription,
defaultValue: input.defaultValue,
maxLength: input.maxLength,
max: input.max,
min: input.min,
dynamicParamDefaultValue: input.dynamicParamDefaultValue
isToolInput: !!toolDescription
})
}
/>

View File

@@ -106,7 +106,6 @@ const VariableTable = ({
keys={keys}
onClose={onCloseFieldEdit}
onSubmit={(e) => {
console.log(e);
if (!!createField && onCreate) {
onCreate(e);
} else if (!!editField && onEdit) {

View File

@@ -322,7 +322,6 @@ const WorkflowContextProvider = ({
item.key === props.key ? props.value : item
);
} else if (type === 'replaceInput') {
onDelEdge({ nodeId, targetHandle: getHandleId(nodeId, 'target', props.key) });
const oldInputIndex = node.data.inputs.findIndex((item) => item.key === props.key);
updateObj.inputs = node.data.inputs.filter((item) => item.key !== props.key);
setTimeout(() => {
@@ -351,7 +350,6 @@ const WorkflowContextProvider = ({
}
}
} else if (type === 'delInput') {
onDelEdge({ nodeId, targetHandle: getHandleId(nodeId, 'target', props.key) });
updateObj.inputs = node.data.inputs.filter((item) => item.key !== props.key);
} else if (type === 'updateOutput') {
updateObj.outputs = node.data.outputs.map((item) =>

View File

@@ -24,6 +24,15 @@ export const flowNode2StoreNodes = ({
outputs: item.data.outputs,
pluginId: item.data.pluginId
}));
// get all handle
const reactFlowViewport = document.querySelector('.react-flow__viewport');
// Gets the value of data-handleid on all elements below it whose data-handleid is not empty
const handleList =
reactFlowViewport?.querySelectorAll('[data-handleid]:not([data-handleid=""])') || [];
const handleIdList = Array.from(handleList).map(
(item) => item.getAttribute('data-handleid') || ''
);
const formatEdges: StoreEdgeItemType[] = edges
.map((item) => ({
source: item.source,
@@ -31,7 +40,11 @@ export const flowNode2StoreNodes = ({
sourceHandle: item.sourceHandle || '',
targetHandle: item.targetHandle || ''
}))
.filter((item) => item.sourceHandle && item.targetHandle);
.filter((item) => item.sourceHandle && item.targetHandle)
.filter(
// Filter out edges that do not have both sourceHandle and targetHandle
(item) => handleIdList.includes(item.sourceHandle) && handleIdList.includes(item.targetHandle)
);
return {
nodes: formatNodes,

View File

@@ -131,7 +131,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
return null;
},
[isShowVersionHistories, edges, updateAppDetail, app._id, t]
[isV2Workflow, isShowVersionHistories, edges, updateAppDetail, app._id, t]
);
const onclickPublish = useCallback(async () => {

View File

@@ -196,7 +196,10 @@ export async function getServerSideProps(context: any) {
const currentTab = context?.query?.currentTab || TabEnum.simpleEdit;
return {
props: { currentTab, ...(await serviceSideProps(context, ['app', 'file', 'publish'])) }
props: {
currentTab,
...(await serviceSideProps(context, ['app', 'file', 'publish', 'workflow']))
}
};
}

View File

@@ -91,7 +91,7 @@ export async function getServerSideProps(context: any) {
return {
props: {
pluginId: context?.query?.pluginId || '',
...(await serviceSideProps(context))
...(await serviceSideProps(context, ['app', 'workflow']))
}
};
}

View File

@@ -20,6 +20,7 @@ import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
import { EditFormType } from './type';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
export const defaultForm: EditFormType = {
@@ -33,7 +34,7 @@ export const defaultForm: EditFormType = {
nodeId: nanoid(),
name: '自定义插件输入',
avatar: '/imgs/workflow/input.png',
flowNodeType: 'pluginInput',
flowNodeType: FlowNodeTypeEnum.pluginInput,
showStatus: false,
position: {
x: 616.4226348688949,
@@ -47,7 +48,7 @@ export const defaultForm: EditFormType = {
nodeId: nanoid(),
name: '自定义插件输出',
avatar: '/imgs/workflow/output.png',
flowNodeType: 'pluginOutput',
flowNodeType: FlowNodeTypeEnum.pluginOutput,
showStatus: false,
position: {
x: 1607.7142331269126,

View File

@@ -4,6 +4,7 @@ import dataset from '../../i18n/zh/dataset.json';
import app from '../../i18n/zh/app.json';
import file from '../../i18n/zh/file.json';
import publish from '../../i18n/zh/publish.json';
import workflow from '../../i18n/zh/workflow.json';
export interface I18nNamespaces {
common: typeof common;
@@ -11,6 +12,7 @@ export interface I18nNamespaces {
app: typeof app;
file: typeof file;
publish: typeof publish;
workflow: typeof workflow;
}
export type I18nNsType = (keyof I18nNamespaces)[];

View File

@@ -8,6 +8,7 @@ type I18nContextType = {
datasetT: TFunction<['dataset'], undefined>;
fileT: TFunction<['file'], undefined>;
publishT: TFunction<['publish'], undefined>;
workflowT: TFunction<['workflow'], undefined>;
};
export const I18nContext = createContext<I18nContextType>({
@@ -21,6 +22,7 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
const { t: datasetT } = useTranslation('dataset');
const { t: fileT } = useTranslation('file');
const { t: publishT } = useTranslation('publish');
const { t: workflowT } = useTranslation('workflow');
return (
<I18nContext.Provider
@@ -29,7 +31,8 @@ const I18nContextProvider = ({ children }: { children: React.ReactNode }) => {
appT,
datasetT,
fileT,
publishT
publishT,
workflowT
}}
>
{children}

View File

@@ -1,7 +1,10 @@
import { AppItemType } from '@/types/app';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import {
FlowNodeInputTypeEnum,
FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
// template
export const appTemplates: (AppItemType & {
@@ -21,7 +24,7 @@ export const appTemplates: (AppItemType & {
name: '系统配置',
intro: '可以配置应用的系统参数',
avatar: '/imgs/workflow/userGuide.png',
flowNodeType: 'userGuide',
flowNodeType: FlowNodeTypeEnum.systemConfig,
position: {
x: 531.2422736065552,
y: -486.7611729549753
@@ -84,7 +87,7 @@ export const appTemplates: (AppItemType & {
name: '流程开始',
intro: '',
avatar: '/imgs/workflow/userChatInput.svg',
flowNodeType: 'workflowStart',
flowNodeType: FlowNodeTypeEnum.workflowStart,
position: {
x: 558.4082376415505,
y: 123.72387429194112
@@ -115,7 +118,7 @@ export const appTemplates: (AppItemType & {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 1097.7317280958762,
@@ -251,7 +254,7 @@ export const appTemplates: (AppItemType & {
name: '系统配置',
intro: '可以配置应用的系统参数',
avatar: '/imgs/workflow/userGuide.png',
flowNodeType: 'userGuide',
flowNodeType: FlowNodeTypeEnum.systemConfig,
position: {
x: 496.57560693988853,
y: -490.7611729549753
@@ -331,7 +334,7 @@ export const appTemplates: (AppItemType & {
name: '流程开始',
intro: '',
avatar: '/imgs/workflow/userChatInput.svg',
flowNodeType: 'workflowStart',
flowNodeType: FlowNodeTypeEnum.workflowStart,
position: {
x: 558.4082376415505,
y: 123.72387429194112
@@ -362,7 +365,7 @@ export const appTemplates: (AppItemType & {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 1097.7317280958762,
@@ -498,7 +501,7 @@ export const appTemplates: (AppItemType & {
name: '系统配置',
intro: '可以配置应用的系统参数',
avatar: '/imgs/workflow/userGuide.png',
flowNodeType: 'userGuide',
flowNodeType: FlowNodeTypeEnum.systemConfig,
position: {
x: 531.2422736065552,
y: -486.7611729549753
@@ -561,7 +564,7 @@ export const appTemplates: (AppItemType & {
name: '流程开始',
intro: '',
avatar: '/imgs/workflow/userChatInput.svg',
flowNodeType: 'workflowStart',
flowNodeType: FlowNodeTypeEnum.workflowStart,
position: {
x: 558.4082376415505,
y: 123.72387429194112
@@ -592,7 +595,7 @@ export const appTemplates: (AppItemType & {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 1638.509551404687,
@@ -712,7 +715,7 @@ export const appTemplates: (AppItemType & {
name: '知识库搜索',
intro: '调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容',
avatar: '/imgs/workflow/db.png',
flowNodeType: 'datasetSearchNode',
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
showStatus: true,
position: {
x: 918.5901682164496,
@@ -826,7 +829,7 @@ export const appTemplates: (AppItemType & {
name: '系统配置',
intro: '可以配置应用的系统参数',
avatar: '/imgs/workflow/userGuide.png',
flowNodeType: 'userGuide',
flowNodeType: FlowNodeTypeEnum.systemConfig,
position: {
x: 531.2422736065552,
y: -486.7611729549753
@@ -889,7 +892,7 @@ export const appTemplates: (AppItemType & {
name: '流程开始',
intro: '',
avatar: '/imgs/workflow/userChatInput.svg',
flowNodeType: 'workflowStart',
flowNodeType: FlowNodeTypeEnum.workflowStart,
position: {
x: 558.4082376415505,
y: 123.72387429194112
@@ -920,7 +923,7 @@ export const appTemplates: (AppItemType & {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 2701.1267277679685,
@@ -1041,7 +1044,7 @@ export const appTemplates: (AppItemType & {
intro:
'根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题',
avatar: '/imgs/workflow/cq.png',
flowNodeType: 'classifyQuestion',
flowNodeType: FlowNodeTypeEnum.classifyQuestion,
showStatus: true,
position: {
x: 1020.9667229609946,
@@ -1123,7 +1126,7 @@ export const appTemplates: (AppItemType & {
intro:
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',
avatar: '/imgs/workflow/reply.png',
flowNodeType: 'answerNode',
flowNodeType: FlowNodeTypeEnum.answerNode,
position: {
x: 1874.9167551056487,
y: 434.98431875888207
@@ -1148,7 +1151,7 @@ export const appTemplates: (AppItemType & {
name: '知识库搜索',
intro: '调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容',
avatar: '/imgs/workflow/db.png',
flowNodeType: 'datasetSearchNode',
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
showStatus: true,
position: {
x: 1851.010152279949,

View File

@@ -75,7 +75,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: '流程开始',
intro: '',
avatar: '/imgs/workflow/userChatInput.svg',
flowNodeType: 'workflowStart',
flowNodeType: FlowNodeTypeEnum.workflowStart,
position: {
x: 558.4082376415505,
y: 123.72387429194112
@@ -111,7 +111,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 1106.3238387960757,
@@ -244,7 +244,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: 'AI 对话',
intro: 'AI 大模型对话',
avatar: '/imgs/workflow/AI.png',
flowNodeType: 'chatNode',
flowNodeType: FlowNodeTypeEnum.chatNode,
showStatus: true,
position: {
x: 1638.509551404687,
@@ -364,7 +364,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: '知识库搜索',
intro: '调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容',
avatar: '/imgs/workflow/db.png',
flowNodeType: 'datasetSearchNode',
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
showStatus: true,
position: {
x: 918.5901682164496,
@@ -483,7 +483,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: '知识库搜索',
intro: '调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容',
avatar: '/imgs/workflow/db.png',
flowNodeType: 'datasetSearchNode',
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
showStatus: true,
position: {
x: 500,
@@ -626,7 +626,7 @@ export function form2AppWorkflow(data: AppSimpleEditFormType): WorkflowType {
name: '工具调用(实验)',
intro: '通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。',
avatar: '/imgs/workflow/tool.svg',
flowNodeType: 'tools',
flowNodeType: FlowNodeTypeEnum.tools,
showStatus: true,
position: {
x: 1062.1738942532802,

View File

@@ -70,7 +70,9 @@ export const FlowValueTypeMap = {
[WorkflowIOValueTypeEnum.selectDataset]: {
label: '选择知识库',
value: WorkflowIOValueTypeEnum.selectDataset,
description: ''
description: `{
datasetId: string;
}`
},
[WorkflowIOValueTypeEnum.any]: {
label: 'any',

View File

@@ -323,7 +323,12 @@ export const updateFlowNodeVersion = (
});
}
const updatedNode: FlowNodeItemType = { ...node, ...template, name: node.name };
const updatedNode: FlowNodeItemType = {
...node,
...template,
name: node.name,
intro: node.intro
};
if (node.inputs && template.inputs) {
updatedNode.inputs = updateArrayBasedOnTemplate(node.inputs, template.inputs);