perf: flow value type

This commit is contained in:
archer
2023-07-27 12:10:30 +08:00
parent 97c7984dd1
commit 118e333600
15 changed files with 292 additions and 79 deletions

View File

@@ -4,7 +4,8 @@ import {
FlowModuleTypeEnum,
FlowInputItemTypeEnum,
FlowOutputItemTypeEnum,
SpecialInputKeyEnum
SpecialInputKeyEnum,
FlowValueTypeEnum
} from './index';
import type { AppItemType } from '@/types/app';
import type { FlowModuleTemplateType } from '@/types/flow';
@@ -56,7 +57,7 @@ export const UserGuideModule: FlowModuleTemplateType = {
};
export const UserInputModule: FlowModuleTemplateType = {
logo: '/imgs/module/userChatInput.png',
name: '用户问题',
name: '用户问题(对话入口)',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
flowType: FlowModuleTypeEnum.questionInput,
url: '/app/modules/init/userChatInput',
@@ -72,6 +73,7 @@ export const UserInputModule: FlowModuleTemplateType = {
key: SystemInputEnum.userChatInput,
label: '用户问题',
type: FlowOutputItemTypeEnum.source,
valueType: FlowValueTypeEnum.string,
targets: []
}
]
@@ -101,6 +103,7 @@ export const HistoryModule: FlowModuleTemplateType = {
{
key: SystemInputEnum.history,
label: '聊天记录',
valueType: FlowValueTypeEnum.chatHistory,
type: FlowOutputItemTypeEnum.source,
targets: []
}
@@ -155,6 +158,7 @@ export const ChatModule: FlowModuleTemplateType = {
key: 'systemPrompt',
type: FlowInputItemTypeEnum.textarea,
label: '系统提示词',
valueType: FlowValueTypeEnum.string,
description: ChatModelSystemTip,
placeholder: ChatModelSystemTip,
value: ''
@@ -162,6 +166,7 @@ export const ChatModule: FlowModuleTemplateType = {
{
key: 'limitPrompt',
type: FlowInputItemTypeEnum.textarea,
valueType: FlowValueTypeEnum.string,
label: '限定词',
description: ChatModelLimitTip,
placeholder: ChatModelLimitTip,
@@ -171,7 +176,8 @@ export const ChatModule: FlowModuleTemplateType = {
{
key: 'quoteQA',
type: FlowInputItemTypeEnum.target,
label: '引用内容'
label: '引用内容',
valueType: FlowValueTypeEnum.kbQuote
},
Input_Template_History,
Input_Template_UserChatInput
@@ -183,6 +189,14 @@ export const ChatModule: FlowModuleTemplateType = {
description: '直接响应,无需配置',
type: FlowOutputItemTypeEnum.hidden,
targets: []
},
{
key: 'finish',
label: '回复结束',
description: 'AI 回复完成后触发',
valueType: FlowValueTypeEnum.boolean,
type: FlowOutputItemTypeEnum.source,
targets: []
}
]
};
@@ -236,12 +250,14 @@ export const KBSearchModule: FlowModuleTemplateType = {
key: 'isEmpty',
label: '搜索结果为空',
type: FlowOutputItemTypeEnum.source,
valueType: FlowValueTypeEnum.boolean,
targets: []
},
{
key: 'unEmpty',
label: '搜索结果不为空',
type: FlowOutputItemTypeEnum.source,
valueType: FlowValueTypeEnum.boolean,
targets: []
},
{
@@ -249,6 +265,7 @@ export const KBSearchModule: FlowModuleTemplateType = {
label: '引用内容',
description: '搜索结果为空时不返回',
type: FlowOutputItemTypeEnum.source,
valueType: FlowValueTypeEnum.kbQuote,
targets: []
}
]
@@ -257,7 +274,8 @@ export const KBSearchModule: FlowModuleTemplateType = {
export const AnswerModule: FlowModuleTemplateType = {
logo: '/imgs/module/reply.png',
name: '指定回复',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示',
description: '该模块可以直接回复一段指定的内容。常用于引导、提示',
flowType: FlowModuleTypeEnum.answerNode,
inputs: [
Input_Template_TFSwitch,
@@ -265,7 +283,8 @@ export const AnswerModule: FlowModuleTemplateType = {
key: SpecialInputKeyEnum.answerText,
value: '',
type: FlowInputItemTypeEnum.textarea,
label: '回复的内容'
label: '回复的内容',
description: '可以使用 \\n 来实现换行'
}
],
outputs: []
@@ -309,6 +328,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
{
key: 'systemPrompt',
type: FlowInputItemTypeEnum.textarea,
valueType: FlowValueTypeEnum.string,
label: '系统提示词',
description:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',

View File

@@ -1,3 +1,5 @@
import type { BoxProps } from '@chakra-ui/react';
export enum FlowInputItemTypeEnum {
systemInput = 'systemInput', // history, userChatInput, variableInput
input = 'input',
@@ -35,6 +37,36 @@ export enum SpecialInputKeyEnum {
'answerText' = 'text'
}
export enum FlowValueTypeEnum {
'string' = 'string',
'number' = 'number',
'boolean' = 'boolean',
'chatHistory' = 'chatHistory',
'kbQuote' = 'kbQuote',
'other' = 'other'
}
export const FlowValueTypeStyle: Record<`${FlowValueTypeEnum}`, BoxProps> = {
[FlowValueTypeEnum.string]: {
background: '#36ADEF'
},
[FlowValueTypeEnum.number]: {
background: '#FB7C3C'
},
[FlowValueTypeEnum.boolean]: {
background: '#E7D118'
},
[FlowValueTypeEnum.chatHistory]: {
background: '#00A9A6'
},
[FlowValueTypeEnum.kbQuote]: {
background: '#A558C9'
},
[FlowValueTypeEnum.other]: {
background: '#9CA2A8'
}
};
export const initModuleType: Record<string, boolean> = {
[FlowModuleTypeEnum.historyNode]: true,
[FlowModuleTypeEnum.questionInput]: true

View File

@@ -1,22 +1,25 @@
import { FlowInputItemType } from '@/types/flow';
import { SystemInputEnum } from '../app';
import { FlowInputItemTypeEnum } from './index';
import { FlowInputItemTypeEnum, FlowValueTypeEnum } from './index';
export const Input_Template_TFSwitch: FlowInputItemType = {
key: SystemInputEnum.switch,
type: FlowInputItemTypeEnum.target,
label: '触发器'
label: '触发器',
valueType: FlowValueTypeEnum.boolean
};
export const Input_Template_History: FlowInputItemType = {
key: SystemInputEnum.history,
type: FlowInputItemTypeEnum.target,
label: '聊天记录'
label: '聊天记录',
valueType: FlowValueTypeEnum.chatHistory
};
export const Input_Template_UserChatInput: FlowInputItemType = {
key: SystemInputEnum.userChatInput,
type: FlowInputItemTypeEnum.target,
label: '用户问题',
required: true
required: true,
valueType: FlowValueTypeEnum.string
};

View File

@@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, App } from '@/service/mongo';
import { EditFormType } from '@/utils/app';
import { AppModuleInputItemType } from '@/types/app';
import { FlowModuleTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
import { TaskResponseKeyEnum } from '@/constants/chat';

View File

@@ -11,7 +11,8 @@ import { Handle, Position } from 'reactflow';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 4);
import MyIcon from '@/components/Icon';
import { FlowOutputItemTypeEnum } from '@/constants/flow';
import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
import SourceHandle from '../render/SourceHandle';
const NodeCQNode = ({
data: { moduleId, inputs, outputs, onChangeNode, ...props }
@@ -82,19 +83,7 @@ const NodeCQNode = ({
});
}}
/>
<Handle
style={{
top: '50%',
right: '-14px',
transform: 'translate(50%,-50%)',
width: '12px',
height: '12px',
background: '#9CA2A8'
}}
type="source"
id={item.key}
position={Position.Right}
/>
<SourceHandle handleKey={item.key} valueType={FlowValueTypeEnum.boolean} />
</Box>
</Box>
</Flex>

View File

@@ -1,31 +1,26 @@
import React from 'react';
import { Handle, Position, NodeProps } from 'reactflow';
import { NodeProps } from 'reactflow';
import { Box } from '@chakra-ui/react';
import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@/types/flow';
import Container from '../modules/Container';
import { SystemInputEnum } from '@/constants/app';
import { FlowValueTypeEnum } from '@/constants/flow';
import SourceHandle from '../render/SourceHandle';
const QuestionInputNode = ({
data: { inputs, outputs, onChangeNode, ...props }
}: NodeProps<FlowModuleItemType>) => {
return (
<NodeCard minW={'220px'} {...props}>
<NodeCard minW={'240px'} {...props}>
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'} textAlign={'end'}>
<Box></Box>
<Handle
style={{
bottom: '0',
right: '0',
transform: 'translate(50%,-5px)',
width: '12px',
height: '12px',
background: '#9CA2A8'
}}
id={SystemInputEnum.userChatInput}
type="source"
position={Position.Right}
/>
<Box position={'relative'}>
<SourceHandle
handleKey={SystemInputEnum.userChatInput}
valueType={FlowValueTypeEnum.string}
/>
</Box>
</Container>
</NodeCard>
);

View File

@@ -37,7 +37,11 @@ const NodeCard = ({
</Box>
{description && (
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
<QuestionOutlineIcon
display={['none', 'inline']}
transform={'translateY(-1px)'}
ml={1}
/>
</MyTooltip>
)}
<Box flex={1} />

View File

@@ -12,10 +12,10 @@ import {
} from '@chakra-ui/react';
import { FlowInputItemTypeEnum } from '@/constants/flow';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Handle, Position } from 'reactflow';
import MySelect from '@/components/Select';
import MySlider from '@/components/Slider';
import MyTooltip from '@/components/MyTooltip';
import TargetHandle from './TargetHandle';
export const Label = ({
required = false,
@@ -35,7 +35,7 @@ export const Label = ({
)}
{description && (
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
<QuestionOutlineIcon display={['none', 'inline']} transform={'translateY(-1px)'} ml={1} />
</MyTooltip>
)}
</Box>
@@ -61,6 +61,10 @@ const RenderBody = ({
{!!item.label && (
<Label required={item.required} description={item.description}>
{item.label}
{(item.type === FlowInputItemTypeEnum.target || item.valueType) && (
<TargetHandle handleKey={item.key} valueType={item.valueType} />
)}
</Label>
)}
<Box mt={2} className={'nodrag'}>
@@ -148,22 +152,6 @@ const RenderBody = ({
{item.type === FlowInputItemTypeEnum.custom && CustomComponent[item.key] && (
<>{CustomComponent[item.key]({ ...item })}</>
)}
{item.type === FlowInputItemTypeEnum.target && (
<Handle
style={{
top: '50%',
left: '-14px',
transform: 'translate(-50%,-50%)',
width: '12px',
height: '12px',
background: '#9CA2A8'
}}
id={item.key}
type="target"
position={Position.Left}
onConnect={(params) => console.log('handle onConnect', params)}
/>
)}
</Box>
</Box>
)

View File

@@ -5,6 +5,7 @@ import { FlowOutputItemTypeEnum } from '@/constants/flow';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Handle, Position } from 'reactflow';
import MyTooltip from '@/components/MyTooltip';
import SourceHandle from './SourceHandle';
const Label = ({
children,
@@ -16,7 +17,7 @@ const Label = ({
<Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}>
{description && (
<MyTooltip label={description} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} mr={1} />
<QuestionOutlineIcon display={['none', 'inline']} transform={'translateY(-1px)'} mr={1} />
</MyTooltip>
)}
{children}
@@ -33,19 +34,7 @@ const RenderBody = ({ flowOutputList }: { flowOutputList: FlowOutputItemType[] }
<Label description={item.description}>{item.label}</Label>
<Box mt={FlowOutputItemTypeEnum.answer ? 0 : 2} className={'nodrag'}>
{item.type === FlowOutputItemTypeEnum.source && (
<Handle
style={{
top: '50%',
right: '-14px',
transform: 'translate(50%,-50%)',
width: '12px',
height: '12px',
background: '#9CA2A8'
}}
type="source"
id={item.key}
position={Position.Right}
/>
<SourceHandle handleKey={item.key} valueType={item.valueType} />
)}
</Box>
</Box>

View File

@@ -0,0 +1,42 @@
import React, { useMemo } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import { Handle, Position } from 'reactflow';
import { FlowValueTypeEnum, FlowValueTypeStyle } from '@/constants/flow';
interface Props extends BoxProps {
handleKey: string;
valueType?: `${FlowValueTypeEnum}`;
}
const SourceHandle = ({ handleKey, valueType, ...props }: Props) => {
const valueStyle = useMemo(
() =>
valueType
? FlowValueTypeStyle[valueType]
: (FlowValueTypeStyle[FlowValueTypeEnum.other] as any),
[]
);
return (
<Box
position={'absolute'}
top={'50%'}
right={'-16px'}
transform={'translate(50%,-50%)'}
{...props}
>
<Handle
style={{
width: '12px',
height: '12px',
...valueStyle
}}
type="source"
id={handleKey}
position={Position.Right}
/>
</Box>
);
};
export default SourceHandle;

View File

@@ -0,0 +1,44 @@
import React, { useMemo } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import { Handle, OnConnect, Position } from 'reactflow';
import { FlowValueTypeEnum, FlowValueTypeStyle } from '@/constants/flow';
interface Props extends BoxProps {
handleKey: string;
valueType?: `${FlowValueTypeEnum}`;
onConnect?: OnConnect;
}
const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
const valueStyle = useMemo(
() =>
valueType
? FlowValueTypeStyle[valueType]
: (FlowValueTypeStyle[FlowValueTypeEnum.other] as any),
[]
);
return (
<Box
position={'absolute'}
top={'50%'}
left={'-16px'}
transform={'translate(50%,-50%)'}
{...props}
>
<Handle
style={{
width: '12px',
height: '12px',
...valueStyle
}}
type="target"
id={handleKey}
datatype={valueStyle}
position={Position.Left}
/>
</Box>
);
};
export default TargetHandle;

View File

@@ -30,6 +30,7 @@ export type ChatProps = {
export type ChatResponse = {
[TaskResponseKeyEnum.answerText]: string;
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
finish: boolean;
};
/* request openai chat */
@@ -143,7 +144,8 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
maxToken,
quoteList: filterQuoteQA,
completeMessages
}
},
finish: true
};
};

View File

@@ -1,7 +1,8 @@
import {
FlowBodyItemTypeEnum,
FlowInputItemTypeEnum,
FlowOutputItemTypeEnum
FlowOutputItemTypeEnum,
FlowValueTypeEnum
} from '@/constants/flow';
import { Connection } from 'reactflow';
import type { AppModuleItemType } from './app';
@@ -18,6 +19,7 @@ export type FlowModuleItemChangeProps = {
export type FlowInputItemType = {
key: string; // 字段名
value?: any;
valueType?: `${FlowValueTypeEnum}`;
type: `${FlowInputItemTypeEnum}`;
label: string;
connected?: boolean;
@@ -39,6 +41,7 @@ export type FlowOutputItemType = {
key: string; // 字段名
label: string;
description?: string;
valueType?: `${FlowValueTypeEnum}`;
type: `${FlowOutputItemTypeEnum}`;
targets: FlowOutputTargetItemType[];
};

View File

@@ -76,10 +76,7 @@ export const appModule2FlowNode = ({
// replace item data
const moduleItem: FlowModuleItemType = {
...item,
logo: template.logo,
name: template.name,
intro: template.intro,
url: template.url,
...template,
inputs: template.inputs.map((templateInput) => {
// use latest inputs
const itemInput = item.inputs.find((item) => item.key === templateInput.key) || templateInput;