This commit is contained in:
Archer
2024-05-28 14:47:10 +08:00
committed by GitHub
parent d9f5f4ede0
commit 9639139b52
58 changed files with 4715 additions and 283 deletions

View File

@@ -1,5 +1,5 @@
import React, { useMemo, useState } from 'react';
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
import React, { useCallback, useMemo, useState } from 'react';
import { Box, useTheme, Flex, Image, BoxProps } from '@chakra-ui/react';
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
import { useTranslation } from 'next-i18next';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
@@ -12,42 +12,68 @@ import Markdown from '../Markdown';
import { QuoteList } from './QuoteModal';
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import { useI18n } from '@/web/context/I18n';
function RowRender({
children,
mb,
label,
...props
}: { children: React.ReactNode; label: string } & BoxProps) {
return (
<Box mb={3}>
<Box fontSize={['sm', 'md']} mb={mb} flex={'0 0 90px'}>
{label}:
</Box>
<Box borderRadius={'sm'} fontSize={'sm'} bg={'myGray.50'} {...props}>
{children}
</Box>
</Box>
);
}
function Row({
label,
value,
rawDom
}: {
label: string;
value?: string | number | boolean;
value?: string | number | boolean | object;
rawDom?: React.ReactNode;
}) {
const { t } = useTranslation();
const theme = useTheme();
const val = value || rawDom;
const strValue = `${value}`;
const isCodeBlock = strValue.startsWith('~~~json');
const isObject = typeof value === 'object';
return val !== undefined && val !== '' && val !== 'undefined' ? (
<Box mb={3}>
<Box fontSize={['sm', 'md']} mb={isCodeBlock ? 0 : 1} flex={'0 0 90px'}>
{t(label)}:
</Box>
<Box
borderRadius={'sm'}
fontSize={'sm'}
bg={'myGray.50'}
{...(isCodeBlock
? { transform: 'translateY(-3px)' }
: value
? { px: 3, py: 2, border: theme.borders.base }
: {})}
>
{value && <Markdown source={strValue} />}
const formatValue = useMemo(() => {
if (isObject) {
return `~~~json\n${JSON.stringify(value, null, 2)}`;
}
return `${value}`;
}, [isObject, value]);
if (rawDom) {
return (
<RowRender label={label} mb={1}>
{rawDom}
</Box>
</Box>
) : null;
</RowRender>
);
}
if (val === undefined || val === '' || val === 'undefined') return null;
return (
<RowRender
label={label}
mb={isObject ? 0 : 1}
{...(isObject
? { transform: 'translateY(-3px)' }
: value
? { px: 3, py: 2, border: theme.borders.base }
: {})}
>
<Markdown source={formatValue} />
</RowRender>
);
}
const WholeResponseModal = ({
@@ -98,6 +124,7 @@ export const ResponseBox = React.memo(function ResponseBox({
}) {
const theme = useTheme();
const { t } = useTranslation();
const { workflowT } = useI18n();
const list = useMemo(
() =>
@@ -251,47 +278,26 @@ export const ResponseBox = React.memo(function ResponseBox({
label={t('core.chat.response.module extract description')}
value={activeModule?.extractDescription}
/>
{activeModule?.extractResult && (
<Row
label={t('core.chat.response.module extract result')}
value={`~~~json\n${JSON.stringify(activeModule?.extractResult, null, 2)}`}
/>
)}
<Row
label={t('core.chat.response.module extract result')}
value={activeModule?.extractResult}
/>
</>
{/* http */}
<>
{activeModule?.headers && (
<Row
label={'Headers'}
value={`~~~json\n${JSON.stringify(activeModule?.headers, null, 2)}`}
/>
)}
{activeModule?.params && (
<Row
label={'Params'}
value={`~~~json\n${JSON.stringify(activeModule?.params, null, 2)}`}
/>
)}
{activeModule?.body && (
<Row label={'Body'} value={`~~~json\n${JSON.stringify(activeModule?.body, null, 2)}`} />
)}
{activeModule?.httpResult && (
<Row
label={t('core.chat.response.module http result')}
value={`~~~json\n${JSON.stringify(activeModule?.httpResult, null, 2)}`}
/>
)}
<Row label={'Headers'} value={activeModule?.headers} />
<Row label={'Params'} value={activeModule?.params} />
<Row label={'Body'} value={activeModule?.body} />
<Row
label={t('core.chat.response.module http result')}
value={activeModule?.httpResult}
/>
</>
{/* plugin */}
<>
{activeModule?.pluginOutput && (
<Row
label={t('core.chat.response.plugin output')}
value={`~~~json\n${JSON.stringify(activeModule?.pluginOutput, null, 2)}`}
/>
)}
<Row label={t('core.chat.response.plugin output')} value={activeModule?.pluginOutput} />
{activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && (
<Row
label={t('core.chat.response.Plugin response detail')}
@@ -310,6 +316,10 @@ export const ResponseBox = React.memo(function ResponseBox({
rawDom={<ResponseBox response={activeModule.toolDetail} showDetail={showDetail} />}
/>
)}
{/* code */}
<Row label={workflowT('response.Custom inputs')} value={activeModule?.customInputs} />
<Row label={workflowT('response.Custom outputs')} value={activeModule?.customOutputs} />
</Box>
</>
);

View File

@@ -58,7 +58,8 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = {
),
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate'))
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')),
[FlowNodeTypeEnum.code]: dynamic(() => import('./nodes/NodeCode'))
};
const edgeTypes = {
[EDGE_TYPE]: ButtonEdge

View File

@@ -0,0 +1,107 @@
import React, { useCallback, useMemo } from 'react';
import { NodeProps } from 'reactflow';
import NodeCard from './render/NodeCard';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
import Container from '../components/Container';
import RenderInput from './render/RenderInput';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { useTranslation } from 'next-i18next';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context';
import IOTitle from '../components/IOTitle';
import RenderToolInput from './render/RenderToolInput';
import RenderOutput from './render/RenderOutput';
import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
import { Box, Flex } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants';
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation();
const { workflowT } = useI18n();
const { nodeId, inputs, outputs } = data;
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const { toolInputs, commonInputs } = splitToolInputs(inputs, nodeId);
const { ConfirmModal, openConfirm } = useConfirm({
content: workflowT('code.Reset template confirm')
});
const CustomComponent = useMemo(
() => ({
[NodeInputKeyEnum.code]: (item: FlowNodeInputItemType) => {
return (
<Box>
<Flex mb={1} alignItems={'flex-end'}>
<Box flex={'1'}>{workflowT('Code')}</Box>
<Box
cursor={'pointer'}
color={'primary.500'}
fontSize={'xs'}
onClick={openConfirm(() => {
onChangeNode({
nodeId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: JS_TEMPLATE
}
});
})}
>
{workflowT('code.Reset template')}
</Box>
</Flex>
<CodeEditor
bg={'white'}
borderRadius={'sm'}
value={item.value}
onChange={(e) => {
onChangeNode({
nodeId,
type: 'updateInput',
key: item.key,
value: {
...item,
value: e
}
});
}}
/>
</Box>
);
}
}),
[nodeId, onChangeNode, openConfirm, workflowT]
);
return (
<NodeCard minW={'400px'} selected={selected} {...data}>
{toolInputs.length > 0 && (
<>
<Container>
<IOTitle text={t('core.module.tool.Tool input')} />
<RenderToolInput nodeId={nodeId} inputs={toolInputs} />
</Container>
</>
)}
<Container>
<IOTitle text={t('common.Input')} />
<RenderInput
nodeId={nodeId}
flowInputList={commonInputs}
CustomComponent={CustomComponent}
/>
</Container>
<Container>
<IOTitle text={t('common.Output')} />
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container>
<ConfirmModal />
</NodeCard>
);
};
export default React.memo(NodeCode);

View File

@@ -72,7 +72,6 @@ const AddInputParam = (props: RenderInputProps) => {
leftIcon={<SmallAddIcon />}
iconSpacing={1}
size={'sm'}
mr={'-5px'}
onClick={() => setEditField(item.dynamicParamDefaultValue ?? {})}
>
{t('common.Add New')}

View File

@@ -13,6 +13,7 @@ import { FlowValueTypeMap } from '@/web/core/workflow/constants/dataType';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/components/core/workflow/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
const RenderList: {
types: `${FlowNodeOutputTypeEnum}`[];
@@ -61,6 +62,7 @@ const RenderOutput = ({
<Box position={'relative'} fontWeight={'medium'}>
{t('core.workflow.Custom outputs')}
</Box>
<QuestionTip ml={1} label={addOutput.description} />
<Box flex={'1 0 0'} />
<Button
variant={'whitePrimary'}

View File

@@ -148,7 +148,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
if ((await targetCol.countDocuments()) > 1) {
// 除了root
console.log('team_members 中有数据,无法自动将 buffer.tts 迁移到 team_members请手动操作');
console.log('team_members 中有数据,无法自动将 team.tts 迁移到 team_members请手动操作');
} else {
await sourceCol.rename('team_members', { dropTarget: true });
console.log('success rename team.members -> team_members');