Files
FastGPT/projects/app/src/utils/app.ts
2023-09-28 17:30:05 +08:00

569 lines
13 KiB
TypeScript

import type { AppModuleItemType, VariableItemType } from '@/types/app';
import { chatModelList } from '@/store/static';
import {
FlowInputItemTypeEnum,
FlowModuleTypeEnum,
FlowValueTypeEnum,
SpecialInputKeyEnum
} from '@/constants/flow';
import { SystemInputEnum } from '@/constants/app';
import type { SelectedDatasetType } from '@/types/core/dataset';
import type { FlowInputItemType } from '@/types/core/app/flow';
import type { AIChatProps } from '@/types/core/aiChat';
import { getGuideModule, splitGuideModule } from '@/components/ChatBox/utils';
export type EditFormType = {
chatModel: AIChatProps;
kb: {
list: SelectedDatasetType;
searchSimilarity: number;
searchLimit: number;
searchEmptyText: string;
};
guide: {
welcome: {
text: string;
};
};
variables: VariableItemType[];
questionGuide: boolean;
};
export const getDefaultAppForm = (): EditFormType => {
const defaultChatModel = chatModelList[0];
return {
chatModel: {
model: defaultChatModel?.model,
systemPrompt: '',
temperature: 0,
quotePrompt: '',
quoteTemplate: '',
maxToken: defaultChatModel ? defaultChatModel.contextMaxToken / 2 : 4000,
frequency: 0.5,
presence: -0.5
},
kb: {
list: [],
searchSimilarity: 0.4,
searchLimit: 5,
searchEmptyText: ''
},
guide: {
welcome: {
text: ''
}
},
variables: [],
questionGuide: false
};
};
export const appModules2Form = (modules: AppModuleItemType[]) => {
const defaultAppForm = getDefaultAppForm();
const updateVal = ({
formKey,
inputs,
key
}: {
formKey: string;
inputs: FlowInputItemType[];
key: string;
}) => {
const propertyPath = formKey.split('.');
let currentObj: any = defaultAppForm;
for (let i = 0; i < propertyPath.length - 1; i++) {
currentObj = currentObj[propertyPath[i]];
}
const val =
inputs.find((item) => item.key === key)?.value ||
currentObj[propertyPath[propertyPath.length - 1]];
currentObj[propertyPath[propertyPath.length - 1]] = val;
};
modules.forEach((module) => {
if (module.flowType === FlowModuleTypeEnum.chatNode) {
updateVal({
formKey: 'chatModel.model',
inputs: module.inputs,
key: 'model'
});
updateVal({
formKey: 'chatModel.temperature',
inputs: module.inputs,
key: 'temperature'
});
updateVal({
formKey: 'chatModel.maxToken',
inputs: module.inputs,
key: 'maxToken'
});
updateVal({
formKey: 'chatModel.systemPrompt',
inputs: module.inputs,
key: 'systemPrompt'
});
updateVal({
formKey: 'chatModel.quoteTemplate',
inputs: module.inputs,
key: 'quoteTemplate'
});
updateVal({
formKey: 'chatModel.quotePrompt',
inputs: module.inputs,
key: 'quotePrompt'
});
} else if (module.flowType === FlowModuleTypeEnum.kbSearchNode) {
updateVal({
formKey: 'kb.list',
inputs: module.inputs,
key: 'kbList'
});
updateVal({
formKey: 'kb.searchSimilarity',
inputs: module.inputs,
key: 'similarity'
});
updateVal({
formKey: 'kb.searchLimit',
inputs: module.inputs,
key: 'limit'
});
// empty text
const emptyOutputs = module.outputs.find((item) => item.key === 'isEmpty')?.targets || [];
const emptyOutput = emptyOutputs[0];
if (emptyOutput) {
const target = modules.find((item) => item.moduleId === emptyOutput.moduleId);
defaultAppForm.kb.searchEmptyText =
target?.inputs?.find((item) => item.key === SpecialInputKeyEnum.answerText)?.value || '';
}
} else if (module.flowType === FlowModuleTypeEnum.userGuide) {
const { welcomeText, variableModules, questionGuide } = splitGuideModule(
getGuideModule(modules)
);
if (welcomeText) {
defaultAppForm.guide.welcome = {
text: welcomeText
};
}
defaultAppForm.variables = variableModules;
defaultAppForm.questionGuide = !!questionGuide;
}
});
return defaultAppForm;
};
const chatModelInput = (formData: EditFormType): FlowInputItemType[] => [
{
key: 'model',
value: formData.chatModel.model,
type: 'custom',
label: '对话模型',
connected: true
},
{
key: 'temperature',
value: formData.chatModel.temperature,
type: 'slider',
label: '温度',
connected: true
},
{
key: 'maxToken',
value: formData.chatModel.maxToken,
type: 'custom',
label: '回复上限',
connected: true
},
{
key: 'systemPrompt',
value: formData.chatModel.systemPrompt || '',
type: 'textarea',
label: '系统提示词',
connected: true
},
{
key: 'quoteTemplate',
value: formData.chatModel.quoteTemplate || '',
type: 'hidden',
label: '引用内容模板',
connected: true
},
{
key: 'quotePrompt',
value: formData.chatModel.quotePrompt || '',
type: 'hidden',
label: '引用内容提示词',
connected: true
},
{
key: 'switch',
type: 'target',
label: '触发器',
connected: formData.kb.list.length > 0
},
{
key: 'quoteQA',
type: 'target',
label: '引用内容',
connected: formData.kb.list.length > 0
},
{
key: 'history',
type: 'target',
label: '聊天记录',
connected: true
},
{
key: 'userChatInput',
type: 'target',
label: '用户问题',
connected: true
}
];
const userGuideTemplate = (formData: EditFormType): AppModuleItemType[] => [
{
name: '用户引导',
flowType: FlowModuleTypeEnum.userGuide,
inputs: [
{
key: SystemInputEnum.welcomeText,
type: FlowInputItemTypeEnum.hidden,
label: '开场白',
value: formData.guide.welcome.text
},
{
key: SystemInputEnum.variables,
type: FlowInputItemTypeEnum.hidden,
label: '对话框变量',
value: formData.variables
},
{
key: SystemInputEnum.questionGuide,
type: FlowInputItemTypeEnum.hidden,
label: '问题引导',
value: formData.questionGuide
}
],
outputs: [],
position: {
x: 447.98520778293346,
y: 721.4016845336229
},
moduleId: 'userGuide'
}
];
const simpleChatTemplate = (formData: EditFormType): AppModuleItemType[] => [
{
name: '用户问题(对话入口)',
flowType: FlowModuleTypeEnum.questionInput,
inputs: [
{
key: 'userChatInput',
connected: true,
label: '用户问题',
type: 'target'
}
],
outputs: [
{
key: 'userChatInput',
targets: [
{
moduleId: 'chatModule',
key: 'userChatInput'
}
]
}
],
position: {
x: 464.32198615344566,
y: 1602.2698463081606
},
moduleId: 'userChatInput'
},
{
name: '聊天记录',
flowType: FlowModuleTypeEnum.historyNode,
inputs: [
{
key: 'maxContext',
value: 6,
connected: true,
type: 'numberInput',
label: '最长记录数'
},
{
key: 'history',
type: 'hidden',
label: '聊天记录',
connected: true
}
],
outputs: [
{
key: 'history',
targets: [
{
moduleId: 'chatModule',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'history'
},
{
name: 'AI 对话',
flowType: FlowModuleTypeEnum.chatNode,
inputs: chatModelInput(formData),
showStatus: true,
outputs: [
{
key: 'answerText',
label: '模型回复',
description: '直接响应,无需配置',
type: 'hidden',
targets: []
},
{
key: 'finish',
label: '回复结束',
description: 'AI 回复完成后触发',
valueType: 'boolean',
type: 'source',
targets: []
}
],
position: {
x: 981.9682828103937,
y: 890.014595014464
},
moduleId: 'chatModule'
}
];
const kbTemplate = (formData: EditFormType): AppModuleItemType[] => [
{
name: '用户问题(对话入口)',
flowType: FlowModuleTypeEnum.questionInput,
inputs: [
{
key: 'userChatInput',
label: '用户问题',
type: 'target',
connected: true
}
],
outputs: [
{
key: 'userChatInput',
targets: [
{
moduleId: 'chatModule',
key: 'userChatInput'
},
{
moduleId: 'kbSearch',
key: 'userChatInput'
}
]
}
],
position: {
x: 464.32198615344566,
y: 1602.2698463081606
},
moduleId: 'userChatInput'
},
{
name: '聊天记录',
flowType: FlowModuleTypeEnum.historyNode,
inputs: [
{
key: 'maxContext',
value: 6,
connected: true,
type: 'numberInput',
label: '最长记录数'
},
{
key: 'history',
type: 'hidden',
label: '聊天记录',
connected: true
}
],
outputs: [
{
key: 'history',
targets: [
{
moduleId: 'chatModule',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'history'
},
{
name: '知识库搜索',
flowType: FlowModuleTypeEnum.kbSearchNode,
showStatus: true,
inputs: [
{
key: 'kbList',
value: formData.kb.list,
type: FlowInputItemTypeEnum.custom,
label: '关联的知识库',
connected: true
},
{
key: 'similarity',
value: formData.kb.searchSimilarity,
type: FlowInputItemTypeEnum.slider,
label: '相似度',
connected: true
},
{
key: 'limit',
value: formData.kb.searchLimit,
type: FlowInputItemTypeEnum.slider,
label: '单次搜索上限',
connected: true
},
{
key: 'switch',
type: FlowInputItemTypeEnum.target,
label: '触发器',
connected: false
},
{
key: 'userChatInput',
type: FlowInputItemTypeEnum.target,
label: '用户问题',
connected: true
}
],
outputs: [
{
key: 'isEmpty',
targets: formData.kb.searchEmptyText
? [
{
moduleId: 'emptyText',
key: 'switch'
}
]
: [
{
moduleId: 'chatModule',
key: 'switch'
}
]
},
{
key: 'unEmpty',
targets: [
{
moduleId: 'chatModule',
key: 'switch'
}
]
},
{
key: 'quoteQA',
targets: [
{
moduleId: 'chatModule',
key: 'quoteQA'
}
]
}
],
position: {
x: 956.0838440206068,
y: 887.462827870246
},
moduleId: 'kbSearch'
},
...(formData.kb.searchEmptyText
? [
{
name: '指定回复',
flowType: FlowModuleTypeEnum.answerNode,
inputs: [
{
key: 'switch',
type: FlowInputItemTypeEnum.target,
label: '触发器',
connected: true
},
{
key: SpecialInputKeyEnum.answerText,
value: formData.kb.searchEmptyText,
type: FlowInputItemTypeEnum.textarea,
valueType: FlowValueTypeEnum.string,
label: '回复的内容',
connected: true
}
],
outputs: [],
position: {
x: 1553.5815811529146,
y: 637.8753731306779
},
moduleId: 'emptyText'
}
]
: []),
{
name: 'AI 对话',
flowType: FlowModuleTypeEnum.chatNode,
inputs: chatModelInput(formData),
showStatus: true,
outputs: [
{
key: 'answerText',
label: '模型回复',
description: '直接响应,无需配置',
type: 'hidden',
targets: []
},
{
key: 'finish',
label: '回复结束',
description: 'AI 回复完成后触发',
valueType: 'boolean',
type: 'source',
targets: []
}
],
position: {
x: 1551.71405495818,
y: 977.4911578918461
},
moduleId: 'chatModule'
}
];
export const appForm2Modules = (formData: EditFormType) => {
const modules = [
...userGuideTemplate(formData),
...(formData.kb.list.length > 0 ? kbTemplate(formData) : simpleChatTemplate(formData))
];
return modules as AppModuleItemType[];
};