mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 17:55:24 +00:00
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:
@@ -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 }]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@@ -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'}>
|
||||
|
@@ -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')),
|
||||
|
@@ -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),
|
||||
|
@@ -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>
|
||||
</>
|
||||
)}
|
||||
|
@@ -55,7 +55,6 @@ const NodeCard = (props: Props) => {
|
||||
maxW = '600px',
|
||||
nodeId,
|
||||
flowNodeType,
|
||||
inputs,
|
||||
selected,
|
||||
menuForbid,
|
||||
isTool = false,
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
@@ -106,7 +106,6 @@ const VariableTable = ({
|
||||
keys={keys}
|
||||
onClose={onCloseFieldEdit}
|
||||
onSubmit={(e) => {
|
||||
console.log(e);
|
||||
if (!!createField && onCreate) {
|
||||
onCreate(e);
|
||||
} else if (!!editField && onEdit) {
|
||||
|
@@ -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) =>
|
||||
|
@@ -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,
|
||||
|
@@ -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 () => {
|
||||
|
@@ -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']))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -91,7 +91,7 @@ export async function getServerSideProps(context: any) {
|
||||
return {
|
||||
props: {
|
||||
pluginId: context?.query?.pluginId || '',
|
||||
...(await serviceSideProps(context))
|
||||
...(await serviceSideProps(context, ['app', 'workflow']))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -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,
|
||||
|
2
projects/app/src/types/i18n.d.ts
vendored
2
projects/app/src/types/i18n.d.ts
vendored
@@ -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)[];
|
||||
|
@@ -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}
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -70,7 +70,9 @@ export const FlowValueTypeMap = {
|
||||
[WorkflowIOValueTypeEnum.selectDataset]: {
|
||||
label: '选择知识库',
|
||||
value: WorkflowIOValueTypeEnum.selectDataset,
|
||||
description: ''
|
||||
description: `{
|
||||
datasetId: string;
|
||||
}`
|
||||
},
|
||||
[WorkflowIOValueTypeEnum.any]: {
|
||||
label: 'any',
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user