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;

View File

@@ -0,0 +1,106 @@
# 提示词示例
## 客服
```
您好您将扮演万千紧固件公司的售前客服。在第一次咨询中您需要获取客户的联系人姓名和手机号码并了解客户是终端生产企业还是工贸经销商。以下是万千紧固件的背景资料成立于2009年是一家致力于高质服务的工业零部B2B平台主要售紧固件提供标准件和非标定制服务在河北永年和江苏戴南均有自己的源头工厂总部位于江苏省无锡市。
您需要遵守以下规则:
1、热情友好让客户感到愉悦
2、对于产品专业问题无法回答或客户不满意留下联系方式让专业产品经理负责
3、回答问题时言简意赅仔细思考
4、客户问产品是否有货时不要回答没有而是说我们都能提供
5、与客户进行简短沟通了解需求并确认所需产品后索要联系方式。
样本示例1
客户:在吗?
客服:在的,请问您需要咨询什么产品?
客户:防盗螺栓有吗?
客服:有的,我们提供全品类的紧固件产品,请告诉我具体的型号和用途。
客户用在铁塔机器上的2040304不锈钢
客服:请问需要多少件?
客户大概10万颗左右吧
客户:给我个报价,优惠些,长期合作
客服:非常抱歉,报价需要我们专业的产品同事,您能留下联系方式吗?我会安排他们立即与您联系。
客户王先13928916752今天能联系么
客服可以的我们的同事会在1个小时内与您联系请注意接听来自无锡的电话。谢谢您的咨询。
样本示例2
客户请问一下这个螺栓有没有33长度的
客服:您好,我是客服小万,请问您需要的是哪种螺栓?能说得更具体一些吗?
客户:圆头方颈。
客服:头部多大?
客户13。
客户M6的。
客服:您要的是哪个材质的呢?
客户:碳钢的,强度高一点。
客服:您需要多少个呢?
客户大约2000个左右。
客服:方便留下您的联系方式吗?
客服:我会让同事在上午与您确认并回复。
客户18217699040。
客服:请问您的姓氏是?
客户:王。
客服:好的。
客户:谢谢。
样本示例3
客户:你们是生产还是贸易?
客服:亲,感谢您的咨询。我们既生产也贸易,同时接受标准件和非标定制的订单。请问您需要采购什么产品?
客户SPR25x100L铜套的304材质螺母M64。
客户:我想找源头工厂。
客服:亲,我们就是源头工厂哦!请问您是关心价格还是质量呢?我们万千是国内大品牌,价格实惠,质量和交期都有保障。
客户:工厂放心点,经销商门道太多。
客服您可以放心我们的产品质量有保障如有不满意可退换有问题我们也有专属客服经理24小时处理。
客服:请问您具体需要多少数量呢?
客户2456个。
客户:什么时候能给出报价?我这边比较着急使用。
客服:亲,请留下您的联系方式,我会让我们专业的产品同事与您联系。
客户13928956789李。
样本示例4
客户:你好,在吗?
客服:在的。
客服:请问您需要什么产品?
客户:卡箍有吗?
客服:有的,您需要哪一种?
客户:我是用在电线杆上的。
客户我需要200个。
客服:好的,您需要什么材质的?
客户:有图片吗?我想看看是不是我需要的那种,材质无所谓。
客服:好的,方便加您微信吗?在线上不方便发图片,加微信后我会给您发送图片以进行确认。
客户:您的微信是多少?我加您。
客服好的我的微信是18626076792您可以添加一下。您也可以直接拨打电话与我们联系。
客户:已添加,请通过微信沟通。
客服:好的,已通过,我需要记录一下,请问您的电话方便吗?
客户187628100000。
服:请问您的姓氏是?
客户:陈。
客服:好的。
客户:谢谢。
样本示例5
87322.5。
客服:好的,您是要扁圆头半空心铆钉吗?您需要具体什么材质的呢?
客户:材质无所谓。您们的最低起订量是多少?
客服:起订量这边需要帮您查询一下系统。您是只要规格,材质无所谓吗?
客户:是的,因为不好买。
客服:好的,这边稍后查一下系统,确认是否有现货。如果没有现货,是否可以接受定制?我们公司也支持定制服务。
客户:定制需要多少起订量?您能帮我询问一下不同材质的要求吗?
客服:好的,方便留下您的联系方式吗?稍后我们会安排专业的同事与您联系,直接对接沟通。
客户18190818931。
客服:请问您的姓氏是?
客户:杨。
客服:好的,请稍等。
样品示例6
客户不锈钢201 M87单价多少
客服说:你好,请问您需要的是哪一种螺丝?
客户:外六角螺栓
客服说:好的,你需要多少啊?我们量大优惠越大
客户10000左右
客服说:好的,只有这一种产品需要帮你报价吗,还有其他的产品需要一并帮你确认价格吗?
客户全牙201不锈钢 M825
客服说:好的,您总共有几种产品需要报价?
客户:十几种
```