mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
v4.6.2-alpah (#511)
This commit is contained in:
@@ -20,7 +20,7 @@ export const splitText2Chunks = (props: { text: string; maxLen: number; overlapL
|
||||
|
||||
4: /(\n\n)/g,
|
||||
5: /([\n])/g,
|
||||
6: /[。]|(?!<[^a-zA-Z])\.\s/g,
|
||||
6: /([。]|(?!<[^a-zA-Z])\.\s)/g,
|
||||
7: /([!?]|!\s|\?\s)/g,
|
||||
8: /([;]|;\s)/g,
|
||||
9: /([,]|,\s)/g
|
||||
@@ -55,10 +55,15 @@ export const splitText2Chunks = (props: { text: string; maxLen: number; overlapL
|
||||
}
|
||||
|
||||
// split text by special char
|
||||
const splitTexts = text
|
||||
.replace(reg, isMarkdownSplit ? `${tempMarker}$1` : `$1${tempMarker}`)
|
||||
.split(`${tempMarker}`)
|
||||
.filter((part) => part);
|
||||
const splitTexts = (() => {
|
||||
if (!reg.test(text)) {
|
||||
return [text];
|
||||
}
|
||||
return text
|
||||
.replace(reg, isMarkdownSplit ? `${tempMarker}$1` : `$1${tempMarker}`)
|
||||
.split(`${tempMarker}`)
|
||||
.filter((part) => part);
|
||||
})();
|
||||
|
||||
let chunks: string[] = [];
|
||||
for (let i = 0; i < splitTexts.length; i++) {
|
||||
|
10
packages/global/core/app/api.d.ts
vendored
10
packages/global/core/app/api.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import type { ChatModelItemType } from '../ai/model.d';
|
||||
import { AppTypeEnum } from './constants';
|
||||
import { AppSchema } from './type';
|
||||
import { AppSchema, AppSimpleEditFormType } from './type';
|
||||
|
||||
export type CreateAppParams = {
|
||||
name?: string;
|
||||
@@ -11,8 +12,15 @@ export type CreateAppParams = {
|
||||
export interface AppUpdateParams {
|
||||
name?: string;
|
||||
type?: `${AppTypeEnum}`;
|
||||
simpleTemplateId?: string;
|
||||
avatar?: string;
|
||||
intro?: string;
|
||||
modules?: AppSchema['modules'];
|
||||
permission?: AppSchema['permission'];
|
||||
}
|
||||
|
||||
export type FormatForm2ModulesProps = {
|
||||
formData: AppSimpleEditFormType;
|
||||
chatModelMaxToken: number;
|
||||
chatModelList: ChatModelItemType[];
|
||||
};
|
||||
|
@@ -1,4 +1,12 @@
|
||||
export enum AppTypeEnum {
|
||||
basic = 'basic',
|
||||
simple = 'simple',
|
||||
advanced = 'advanced'
|
||||
}
|
||||
export const AppTypeMap = {
|
||||
[AppTypeEnum.simple]: {
|
||||
label: 'simple'
|
||||
},
|
||||
[AppTypeEnum.advanced]: {
|
||||
label: 'advanced'
|
||||
}
|
||||
};
|
||||
|
90
packages/global/core/app/type.d.ts
vendored
90
packages/global/core/app/type.d.ts
vendored
@@ -1,6 +1,9 @@
|
||||
import { ModuleItemType } from '../module/type';
|
||||
import type { AppTTSConfigType, ModuleItemType, VariableItemType } from '../module/type.d';
|
||||
import { AppTypeEnum } from './constants';
|
||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||
import type { AIChatModuleProps, DatasetModuleProps } from '../module/node/type.d';
|
||||
import { VariableInputEnum } from '../module/constants';
|
||||
import { SelectedDatasetType } from '../module/api';
|
||||
|
||||
export interface AppSchema {
|
||||
_id: string;
|
||||
@@ -9,6 +12,7 @@ export interface AppSchema {
|
||||
tmbId: string;
|
||||
name: string;
|
||||
type: `${AppTypeEnum}`;
|
||||
simpleTemplateId: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
updateTime: number;
|
||||
@@ -29,3 +33,87 @@ export type AppDetailType = AppSchema & {
|
||||
isOwner: boolean;
|
||||
canWrite: boolean;
|
||||
};
|
||||
|
||||
// export type AppSimpleEditFormType = {
|
||||
// aiSettings: AIChatModuleProps;
|
||||
// dataset: DatasetModuleProps & {
|
||||
// searchEmptyText: string;
|
||||
// };
|
||||
// userGuide: {
|
||||
// welcomeText: string;
|
||||
// variables: VariableItemType[];
|
||||
// questionGuide: boolean;
|
||||
// tts: AppTTSConfigType;
|
||||
// };
|
||||
// };
|
||||
// Since useform cannot infer enumeration types, all enumeration keys can only be undone manually
|
||||
export type AppSimpleEditFormType = {
|
||||
templateId: string;
|
||||
aiSettings: {
|
||||
model: string;
|
||||
systemPrompt?: string | undefined;
|
||||
temperature: number;
|
||||
maxToken: number;
|
||||
isResponseAnswerText: boolean;
|
||||
quoteTemplate?: string | undefined;
|
||||
quotePrompt?: string | undefined;
|
||||
};
|
||||
dataset: {
|
||||
datasets: SelectedDatasetType;
|
||||
similarity: number;
|
||||
limit: number;
|
||||
rerank: boolean;
|
||||
searchEmptyText: string;
|
||||
};
|
||||
userGuide: {
|
||||
welcomeText: string;
|
||||
variables: {
|
||||
id: string;
|
||||
key: string;
|
||||
label: string;
|
||||
type: `${VariableInputEnum}`;
|
||||
required: boolean;
|
||||
maxLen: number;
|
||||
enums: {
|
||||
value: string;
|
||||
}[];
|
||||
}[];
|
||||
questionGuide: boolean;
|
||||
tts: {
|
||||
type: 'none' | 'web' | 'model';
|
||||
model?: string | undefined;
|
||||
voice?: string | undefined;
|
||||
speed?: number | undefined;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* simple mode template*/
|
||||
export type AppSimpleEditConfigTemplateType = {
|
||||
id: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
systemForm: {
|
||||
aiSettings?: {
|
||||
model?: boolean;
|
||||
systemPrompt?: boolean;
|
||||
temperature?: boolean;
|
||||
maxToken?: boolean;
|
||||
quoteTemplate?: boolean;
|
||||
quotePrompt?: boolean;
|
||||
};
|
||||
dataset?: {
|
||||
datasets?: boolean;
|
||||
similarity?: boolean;
|
||||
limit?: boolean;
|
||||
rerank?: boolean;
|
||||
searchEmptyText?: boolean;
|
||||
};
|
||||
userGuide?: {
|
||||
welcomeText?: boolean;
|
||||
variables?: boolean;
|
||||
questionGuide?: boolean;
|
||||
tts?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
123
packages/global/core/app/utils.ts
Normal file
123
packages/global/core/app/utils.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { AppSimpleEditFormType } from '../app/type';
|
||||
import { FlowNodeTypeEnum } from '../module/node/constant';
|
||||
import { ModuleOutputKeyEnum, ModuleInputKeyEnum } from '../module/constants';
|
||||
import type { FlowNodeInputItemType } from '../module/node/type.d';
|
||||
import { getGuideModule, splitGuideModule } from '../module/utils';
|
||||
import { defaultChatModels } from '../ai/model';
|
||||
import { ModuleItemType } from '../module/type.d';
|
||||
|
||||
export const getDefaultAppForm = (templateId = 'fastgpt-universal'): AppSimpleEditFormType => {
|
||||
const defaultChatModel = defaultChatModels[0];
|
||||
|
||||
return {
|
||||
templateId,
|
||||
aiSettings: {
|
||||
model: defaultChatModel?.model,
|
||||
systemPrompt: '',
|
||||
temperature: 0,
|
||||
isResponseAnswerText: true,
|
||||
quotePrompt: '',
|
||||
quoteTemplate: '',
|
||||
maxToken: defaultChatModel ? defaultChatModel.maxResponse / 2 : 4000
|
||||
},
|
||||
dataset: {
|
||||
datasets: [],
|
||||
similarity: 0.4,
|
||||
limit: 5,
|
||||
searchEmptyText: '',
|
||||
rerank: false
|
||||
},
|
||||
userGuide: {
|
||||
welcomeText: '',
|
||||
variables: [],
|
||||
questionGuide: false,
|
||||
tts: {
|
||||
type: 'web'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* format app modules to edit form */
|
||||
export const appModules2Form = ({
|
||||
templateId,
|
||||
modules
|
||||
}: {
|
||||
modules: ModuleItemType[];
|
||||
templateId: string;
|
||||
}) => {
|
||||
const defaultAppForm = getDefaultAppForm(templateId);
|
||||
|
||||
const findInputValueByKey = (inputs: FlowNodeInputItemType[], key: string) => {
|
||||
return inputs.find((item) => item.key === key)?.value;
|
||||
};
|
||||
|
||||
modules.forEach((module) => {
|
||||
if (module.flowType === FlowNodeTypeEnum.chatNode) {
|
||||
defaultAppForm.aiSettings.model = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiModel
|
||||
);
|
||||
defaultAppForm.aiSettings.systemPrompt = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiSystemPrompt
|
||||
);
|
||||
defaultAppForm.aiSettings.temperature = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiChatTemperature
|
||||
);
|
||||
defaultAppForm.aiSettings.maxToken = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiChatMaxToken
|
||||
);
|
||||
defaultAppForm.aiSettings.quoteTemplate = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiChatQuoteTemplate
|
||||
);
|
||||
defaultAppForm.aiSettings.quotePrompt = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.aiChatQuoteTemplate
|
||||
);
|
||||
} else if (module.flowType === FlowNodeTypeEnum.datasetSearchNode) {
|
||||
defaultAppForm.dataset.datasets = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.datasetSelectList
|
||||
);
|
||||
defaultAppForm.dataset.similarity = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.datasetSimilarity
|
||||
);
|
||||
defaultAppForm.dataset.limit = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.datasetLimit
|
||||
);
|
||||
defaultAppForm.dataset.rerank = findInputValueByKey(
|
||||
module.inputs,
|
||||
ModuleInputKeyEnum.datasetStartReRank
|
||||
);
|
||||
|
||||
// empty text
|
||||
const emptyOutputs =
|
||||
module.outputs.find((item) => item.key === ModuleOutputKeyEnum.datasetIsEmpty)?.targets ||
|
||||
[];
|
||||
const emptyOutput = emptyOutputs[0];
|
||||
if (emptyOutput) {
|
||||
const target = modules.find((item) => item.moduleId === emptyOutput.moduleId);
|
||||
defaultAppForm.dataset.searchEmptyText =
|
||||
target?.inputs?.find((item) => item.key === ModuleInputKeyEnum.answerText)?.value || '';
|
||||
}
|
||||
} else if (module.flowType === FlowNodeTypeEnum.userGuide) {
|
||||
const { welcomeText, variableModules, questionGuide, ttsConfig } = splitGuideModule(
|
||||
getGuideModule(modules)
|
||||
);
|
||||
defaultAppForm.userGuide = {
|
||||
welcomeText: welcomeText,
|
||||
variables: variableModules,
|
||||
questionGuide: questionGuide,
|
||||
tts: ttsConfig
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return defaultAppForm;
|
||||
};
|
1
packages/global/core/chat/api.d.ts
vendored
1
packages/global/core/chat/api.d.ts
vendored
@@ -30,5 +30,4 @@ export type InitChatResponse = {
|
||||
export type ChatHistoryItemResType = moduleDispatchResType & {
|
||||
moduleType: `${FlowNodeTypeEnum}`;
|
||||
moduleName: string;
|
||||
moduleLogo?: string;
|
||||
};
|
||||
|
@@ -6,12 +6,6 @@ export enum ChatRoleEnum {
|
||||
Tool = 'Tool'
|
||||
}
|
||||
|
||||
export enum TaskResponseKeyEnum {
|
||||
'answerText' = 'answerText', // answer module text key
|
||||
'responseData' = 'responseData',
|
||||
'history' = 'history'
|
||||
}
|
||||
|
||||
export const ChatRoleMap = {
|
||||
[ChatRoleEnum.System]: {
|
||||
name: '系统提示词'
|
||||
|
16
packages/global/core/chat/type.d.ts
vendored
16
packages/global/core/chat/type.d.ts
vendored
@@ -1,8 +1,9 @@
|
||||
import { ClassifyQuestionAgentItemType } from '../module/type';
|
||||
import { SearchDataResponseItemType } from '../dataset/type';
|
||||
import { ChatRoleEnum, ChatSourceEnum, TaskResponseKeyEnum } from './constants';
|
||||
import { ChatRoleEnum, ChatSourceEnum } from './constants';
|
||||
import { FlowNodeTypeEnum } from '../module/node/constant';
|
||||
import { AppSchema } from 'core/app/type';
|
||||
import { ModuleOutputKeyEnum } from '../module/constants';
|
||||
import { AppSchema } from '../app/type';
|
||||
|
||||
export type ChatSchema = {
|
||||
_id: string;
|
||||
@@ -38,7 +39,7 @@ export type ChatItemSchema = {
|
||||
value: string;
|
||||
userFeedback?: string;
|
||||
adminFeedback?: AdminFbkType;
|
||||
[TaskResponseKeyEnum.responseData]?: ChatHistoryItemResType[];
|
||||
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
|
||||
};
|
||||
|
||||
export type AdminFbkType = {
|
||||
@@ -55,14 +56,14 @@ export type ChatItemType = {
|
||||
value: any;
|
||||
userFeedback?: string;
|
||||
adminFeedback?: ChatItemSchema['feedback'];
|
||||
[TaskResponseKeyEnum.responseData]?: ChatItemSchema[TaskResponseKeyEnum.responseData];
|
||||
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
|
||||
};
|
||||
|
||||
export type ChatSiteItemType = {
|
||||
export type ChatSiteItemType = ChatItemType & {
|
||||
status: 'loading' | 'running' | 'finish';
|
||||
moduleName?: string;
|
||||
ttsBuffer?: Uint8Array;
|
||||
} & ChatItemType;
|
||||
};
|
||||
|
||||
export type HistoryItemType = {
|
||||
chatId: string;
|
||||
@@ -77,13 +78,14 @@ export type ChatHistoryItemType = HistoryItemType & {
|
||||
|
||||
// response data
|
||||
export type moduleDispatchResType = {
|
||||
moduleLogo?: string;
|
||||
price: number;
|
||||
runningTime?: number;
|
||||
tokens?: number;
|
||||
model?: string;
|
||||
query?: string;
|
||||
|
||||
// chat
|
||||
question?: string;
|
||||
temperature?: number;
|
||||
maxToken?: number;
|
||||
quoteList?: SearchDataResponseItemType[];
|
||||
|
102
packages/global/core/module/constants.ts
Normal file
102
packages/global/core/module/constants.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
export enum ModuleTemplateTypeEnum {
|
||||
userGuide = 'userGuide',
|
||||
systemInput = 'systemInput',
|
||||
textAnswer = 'textAnswer',
|
||||
dataset = 'dataset',
|
||||
functionCall = 'functionCall',
|
||||
externalCall = 'externalCall',
|
||||
|
||||
personalPlugin = 'personalPlugin',
|
||||
communityPlugin = 'communityPlugin',
|
||||
commercialPlugin = 'commercialPlugin',
|
||||
|
||||
other = 'other'
|
||||
}
|
||||
|
||||
export enum ModuleDataTypeEnum {
|
||||
string = 'string',
|
||||
number = 'number',
|
||||
boolean = 'boolean',
|
||||
chatHistory = 'chatHistory',
|
||||
datasetQuote = 'datasetQuote',
|
||||
any = 'any',
|
||||
|
||||
// plugin special type
|
||||
selectApp = 'selectApp',
|
||||
selectDataset = 'selectDataset'
|
||||
}
|
||||
|
||||
/* reg: modulename key */
|
||||
export enum ModuleInputKeyEnum {
|
||||
// old
|
||||
welcomeText = 'welcomeText',
|
||||
variables = 'variables',
|
||||
switch = 'switch', // a trigger switch
|
||||
history = 'history',
|
||||
userChatInput = 'userChatInput',
|
||||
questionGuide = 'questionGuide',
|
||||
tts = 'tts',
|
||||
answerText = 'text',
|
||||
agents = 'agents', // cq agent key
|
||||
|
||||
// latest
|
||||
// common
|
||||
aiModel = 'model',
|
||||
aiSystemPrompt = 'systemPrompt',
|
||||
description = 'description',
|
||||
|
||||
// history
|
||||
historyMaxAmount = 'maxContext',
|
||||
|
||||
// ai chat
|
||||
aiChatTemperature = 'temperature',
|
||||
aiChatMaxToken = 'maxToken',
|
||||
aiChatSettingModal = 'aiSettings',
|
||||
aiChatIsResponseText = 'isResponseAnswerText',
|
||||
aiChatQuoteTemplate = 'quoteTemplate',
|
||||
aiChatQuotePrompt = 'quotePrompt',
|
||||
aiChatDatasetQuote = 'quoteQA',
|
||||
|
||||
// dataset
|
||||
datasetSelectList = 'datasets',
|
||||
datasetSimilarity = 'similarity',
|
||||
datasetLimit = 'limit',
|
||||
datasetStartReRank = 'rerank',
|
||||
|
||||
// context extract
|
||||
contextExtractInput = 'content',
|
||||
extractKeys = 'extractKeys',
|
||||
|
||||
// http
|
||||
httpUrl = 'url',
|
||||
|
||||
// app
|
||||
runAppSelectApp = 'app',
|
||||
|
||||
// plugin
|
||||
pluginId = 'pluginId'
|
||||
}
|
||||
|
||||
export enum ModuleOutputKeyEnum {
|
||||
// common
|
||||
userChatInput = 'userChatInput',
|
||||
finish = 'finish',
|
||||
responseData = 'responseData',
|
||||
history = 'history',
|
||||
answerText = 'answerText', // answer module text key
|
||||
success = 'success',
|
||||
failed = 'failed',
|
||||
|
||||
// dataset
|
||||
datasetIsEmpty = 'isEmpty',
|
||||
datasetUnEmpty = 'unEmpty',
|
||||
datasetQuoteQA = 'quoteQA',
|
||||
|
||||
// context extract
|
||||
contextExtractFields = 'fields'
|
||||
}
|
||||
|
||||
export enum VariableInputEnum {
|
||||
input = 'input',
|
||||
select = 'select'
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
export enum FlowNodeInputTypeEnum {
|
||||
systemInput = 'systemInput', // history, userChatInput, variableInput
|
||||
|
||||
input = 'input', // one line input
|
||||
textarea = 'textarea',
|
||||
numberInput = 'numberInput',
|
||||
@@ -8,11 +9,10 @@ export enum FlowNodeInputTypeEnum {
|
||||
custom = 'custom',
|
||||
target = 'target', // data input
|
||||
switch = 'switch',
|
||||
chatInput = 'chatInput',
|
||||
selectApp = 'selectApp',
|
||||
// chat special input
|
||||
aiSettings = 'aiSettings',
|
||||
maxToken = 'maxToken',
|
||||
// maxToken = 'maxToken',
|
||||
selectChatModel = 'selectChatModel',
|
||||
// dataset special input
|
||||
selectDataset = 'selectDataset',
|
||||
@@ -44,18 +44,3 @@ export enum FlowNodeTypeEnum {
|
||||
// abandon
|
||||
variable = 'variable'
|
||||
}
|
||||
|
||||
export enum FlowNodeSpecialInputKeyEnum {
|
||||
'answerText' = 'text',
|
||||
'agents' = 'agents', // cq agent key
|
||||
'pluginId' = 'pluginId'
|
||||
}
|
||||
|
||||
export enum FlowNodeValTypeEnum {
|
||||
'string' = 'string',
|
||||
'number' = 'number',
|
||||
'boolean' = 'boolean',
|
||||
'chatHistory' = 'chatHistory',
|
||||
'datasetQuote' = 'datasetQuote',
|
||||
'any' = 'any'
|
||||
}
|
||||
|
61
packages/global/core/module/node/type.d.ts
vendored
61
packages/global/core/module/node/type.d.ts
vendored
@@ -1,9 +1,6 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeValTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from './constant';
|
||||
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from './constant';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants';
|
||||
import { SelectedDatasetType } from '../api';
|
||||
|
||||
export type FlowNodeChangeProps = {
|
||||
moduleId: string;
|
||||
@@ -23,24 +20,27 @@ export type FlowNodeChangeProps = {
|
||||
};
|
||||
|
||||
export type FlowNodeInputItemType = {
|
||||
key: string; // 字段名
|
||||
key: `${ModuleInputKeyEnum}`;
|
||||
type: `${FlowNodeInputTypeEnum}`; // Decide on a render style
|
||||
value?: any;
|
||||
valueType?: `${FlowNodeValTypeEnum}`;
|
||||
type: `${FlowNodeInputTypeEnum}`;
|
||||
valueType?: `${ModuleDataTypeEnum}`; // data type
|
||||
label: string;
|
||||
edit?: boolean;
|
||||
connected?: boolean;
|
||||
description?: string;
|
||||
placeholder?: string;
|
||||
plusField?: boolean;
|
||||
required?: boolean;
|
||||
edit?: boolean; // Whether to allow editing
|
||||
connected?: boolean; // unConnected field will be deleted
|
||||
|
||||
showTargetInApp?: boolean;
|
||||
showTargetInPlugin?: boolean;
|
||||
|
||||
placeholder?: string; // input,textarea
|
||||
list?: { label: string; value: any }[]; // select
|
||||
step?: number; // slider max?: number;
|
||||
max?: number;
|
||||
min?: number;
|
||||
step?: number;
|
||||
required?: boolean;
|
||||
list?: { label: string; value: any }[];
|
||||
markList?: { label: string; value: any }[];
|
||||
customData?: () => any;
|
||||
valueCheck?: (value: any) => boolean;
|
||||
markList?: { label: string; value: any }[]; // slider
|
||||
|
||||
plusField?: boolean; // plus system will show
|
||||
};
|
||||
|
||||
export type FlowNodeOutputTargetItemType = {
|
||||
@@ -48,11 +48,30 @@ export type FlowNodeOutputTargetItemType = {
|
||||
key: string;
|
||||
};
|
||||
export type FlowNodeOutputItemType = {
|
||||
key: string; // 字段名
|
||||
key: `${ModuleOutputKeyEnum}` | string;
|
||||
label?: string;
|
||||
edit?: boolean;
|
||||
description?: string;
|
||||
valueType?: `${FlowNodeValTypeEnum}`;
|
||||
valueType?: `${ModuleDataTypeEnum}`;
|
||||
type?: `${FlowNodeOutputTypeEnum}`;
|
||||
targets: FlowNodeOutputTargetItemType[];
|
||||
};
|
||||
|
||||
/* ------------- item type --------------- */
|
||||
/* ai chat modules props */
|
||||
export type AIChatModuleProps = {
|
||||
[ModuleInputKeyEnum.aiModel]: string;
|
||||
[ModuleInputKeyEnum.aiSystemPrompt]?: string;
|
||||
[ModuleInputKeyEnum.aiChatTemperature]: number;
|
||||
[ModuleInputKeyEnum.aiChatMaxToken]: number;
|
||||
[ModuleInputKeyEnum.aiChatIsResponseText]: boolean;
|
||||
[ModuleInputKeyEnum.aiChatQuoteTemplate]?: string;
|
||||
[ModuleInputKeyEnum.aiChatQuotePrompt]?: string;
|
||||
};
|
||||
|
||||
export type DatasetModuleProps = {
|
||||
[ModuleInputKeyEnum.datasetSelectList]: SelectedDatasetType;
|
||||
[ModuleInputKeyEnum.datasetSimilarity]: number;
|
||||
[ModuleInputKeyEnum.datasetLimit]: number;
|
||||
[ModuleInputKeyEnum.datasetStartReRank]: boolean;
|
||||
};
|
||||
|
32
packages/global/core/module/template/input.ts
Normal file
32
packages/global/core/module/template/input.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { FlowNodeInputItemType } from '../node/type.d';
|
||||
import { ModuleInputKeyEnum } from '../constants';
|
||||
import { FlowNodeInputTypeEnum } from '../node/constant';
|
||||
import { ModuleDataTypeEnum } from '../constants';
|
||||
|
||||
export const Input_Template_TFSwitch: FlowNodeInputItemType = {
|
||||
key: ModuleInputKeyEnum.switch,
|
||||
type: FlowNodeInputTypeEnum.target,
|
||||
label: 'core.module.input.label.switch',
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
};
|
||||
|
||||
export const Input_Template_History: FlowNodeInputItemType = {
|
||||
key: ModuleInputKeyEnum.history,
|
||||
type: FlowNodeInputTypeEnum.target,
|
||||
label: 'core.module.input.label.chat history',
|
||||
valueType: ModuleDataTypeEnum.chatHistory,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
};
|
||||
|
||||
export const Input_Template_UserChatInput: FlowNodeInputItemType = {
|
||||
key: ModuleInputKeyEnum.userChatInput,
|
||||
type: FlowNodeInputTypeEnum.target,
|
||||
label: 'core.module.input.label.user question',
|
||||
required: true,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
};
|
13
packages/global/core/module/template/output.ts
Normal file
13
packages/global/core/module/template/output.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { FlowNodeOutputItemType } from '../node/type';
|
||||
import { ModuleOutputKeyEnum } from '../constants';
|
||||
import { FlowNodeOutputTypeEnum } from '../node/constant';
|
||||
import { ModuleDataTypeEnum } from '../constants';
|
||||
|
||||
export const Output_Template_Finish: FlowNodeOutputItemType = {
|
||||
key: ModuleOutputKeyEnum.finish,
|
||||
label: 'core.module.output.label.running done',
|
||||
description: 'core.module.output.description.running done',
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
};
|
157
packages/global/core/module/template/system/aiChat.ts
Normal file
157
packages/global/core/module/template/system/aiChat.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import {
|
||||
ModuleDataTypeEnum,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum,
|
||||
ModuleTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import {
|
||||
Input_Template_History,
|
||||
Input_Template_TFSwitch,
|
||||
Input_Template_UserChatInput
|
||||
} from '../input';
|
||||
import { chatNodeSystemPromptTip } from '../tip';
|
||||
import { Output_Template_Finish } from '../output';
|
||||
|
||||
export const AiChatModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.chatNode,
|
||||
templateType: ModuleTemplateTypeEnum.textAnswer,
|
||||
flowType: FlowNodeTypeEnum.chatNode,
|
||||
avatar: '/imgs/module/AI.png',
|
||||
name: 'AI 对话',
|
||||
intro: 'AI 大模型对话',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiModel,
|
||||
type: FlowNodeInputTypeEnum.selectChatModel,
|
||||
label: '对话模型',
|
||||
required: true,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
// --- settings modal
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatTemperature,
|
||||
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
|
||||
label: '温度',
|
||||
value: 0,
|
||||
valueType: ModuleDataTypeEnum.number,
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
markList: [
|
||||
{ label: '严谨', value: 0 },
|
||||
{ label: '发散', value: 10 }
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatMaxToken,
|
||||
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
|
||||
label: '回复上限',
|
||||
value: 2000,
|
||||
valueType: ModuleDataTypeEnum.number,
|
||||
min: 100,
|
||||
max: 4000,
|
||||
step: 50,
|
||||
markList: [
|
||||
{ label: '100', value: 100 },
|
||||
{
|
||||
label: `${4000}`,
|
||||
value: 4000
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatIsResponseText,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
label: '返回AI内容',
|
||||
value: true,
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatQuoteTemplate,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
label: '引用内容模板',
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
value: '',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatQuotePrompt,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
label: '引用内容提示词',
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
value: '',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatSettingModal,
|
||||
type: FlowNodeInputTypeEnum.aiSettings,
|
||||
label: '',
|
||||
connected: false,
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
// settings modal ---
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiSystemPrompt,
|
||||
type: FlowNodeInputTypeEnum.textarea,
|
||||
label: '系统提示词',
|
||||
max: 300,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
description: chatNodeSystemPromptTip,
|
||||
placeholder: chatNodeSystemPromptTip,
|
||||
value: '',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiChatDatasetQuote,
|
||||
type: FlowNodeInputTypeEnum.target,
|
||||
label: '引用内容',
|
||||
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
|
||||
valueType: ModuleDataTypeEnum.datasetQuote,
|
||||
connected: false,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleOutputKeyEnum.history,
|
||||
label: '新的上下文',
|
||||
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
|
||||
valueType: ModuleDataTypeEnum.chatHistory,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.answerText,
|
||||
label: 'AI回复',
|
||||
description: '将在 stream 回复完毕后触发',
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
Output_Template_Finish
|
||||
]
|
||||
};
|
@@ -0,0 +1,29 @@
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { Input_Template_TFSwitch } from '../input';
|
||||
import { Output_Template_Finish } from '../output';
|
||||
|
||||
export const AssignedAnswerModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.answerNode,
|
||||
templateType: ModuleTemplateTypeEnum.textAnswer,
|
||||
flowType: FlowNodeTypeEnum.answerNode,
|
||||
avatar: '/imgs/module/reply.png',
|
||||
name: '指定回复',
|
||||
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示',
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.answerText,
|
||||
type: FlowNodeInputTypeEnum.textarea,
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
value: '',
|
||||
label: '回复的内容',
|
||||
description:
|
||||
'可以使用 \\n 来实现连续换行。\n\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n\n如传入非字符串类型数据将会自动转成字符串',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
}
|
||||
],
|
||||
outputs: [Output_Template_Finish]
|
||||
};
|
@@ -0,0 +1,92 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
|
||||
import {
|
||||
Input_Template_History,
|
||||
Input_Template_TFSwitch,
|
||||
Input_Template_UserChatInput
|
||||
} from '../input';
|
||||
|
||||
export const ClassifyQuestionModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.classifyQuestion,
|
||||
templateType: ModuleTemplateTypeEnum.functionCall,
|
||||
flowType: FlowNodeTypeEnum.classifyQuestion,
|
||||
avatar: '/imgs/module/cq.png',
|
||||
name: '问题分类',
|
||||
intro:
|
||||
'根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于 laf 通用问题\n类型3: 关于 laf 代码问题\n类型4: 其他问题',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiModel,
|
||||
type: FlowNodeInputTypeEnum.selectChatModel,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
label: '分类模型',
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.aiSystemPrompt,
|
||||
type: FlowNodeInputTypeEnum.textarea,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
value: '',
|
||||
label: '背景知识',
|
||||
description:
|
||||
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',
|
||||
placeholder: '例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput,
|
||||
{
|
||||
key: ModuleInputKeyEnum.agents,
|
||||
type: FlowNodeInputTypeEnum.custom,
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
label: '',
|
||||
value: [
|
||||
{
|
||||
value: '打招呼',
|
||||
key: 'fasw'
|
||||
},
|
||||
{
|
||||
value: '关于 xxx 的问题',
|
||||
key: 'fqsw'
|
||||
},
|
||||
{
|
||||
value: '其他问题',
|
||||
key: 'fesw'
|
||||
}
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
// custom output
|
||||
{
|
||||
key: 'fasw',
|
||||
label: '',
|
||||
type: FlowNodeOutputTypeEnum.hidden,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'fqsw',
|
||||
label: '',
|
||||
type: FlowNodeOutputTypeEnum.hidden,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'fesw',
|
||||
label: '',
|
||||
type: FlowNodeOutputTypeEnum.hidden,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
};
|
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import {
|
||||
ModuleDataTypeEnum,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum,
|
||||
ModuleTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import { Input_Template_History, Input_Template_TFSwitch } from '../input';
|
||||
|
||||
export const ContextExtractModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.contentExtract,
|
||||
templateType: ModuleTemplateTypeEnum.functionCall,
|
||||
flowType: FlowNodeTypeEnum.contentExtract,
|
||||
avatar: '/imgs/module/extract.png',
|
||||
name: '文本内容提取',
|
||||
intro: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.description,
|
||||
type: FlowNodeInputTypeEnum.textarea,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
value: '',
|
||||
label: '提取要求描述',
|
||||
description: '写一段提取要求,告诉 AI 需要提取哪些内容',
|
||||
required: true,
|
||||
placeholder:
|
||||
'例如: \n1. 你是一个实验室预约助手。根据用户问题,提取出姓名、实验室号和预约时间',
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
Input_Template_History,
|
||||
{
|
||||
key: ModuleInputKeyEnum.contextExtractInput,
|
||||
type: FlowNodeInputTypeEnum.target,
|
||||
label: '需要提取的文本',
|
||||
required: true,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
showTargetInApp: true,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.extractKeys,
|
||||
type: FlowNodeInputTypeEnum.custom,
|
||||
label: '目标字段',
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
|
||||
value: [], // {desc: string; key: string; required: boolean;}[]
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleOutputKeyEnum.success,
|
||||
label: '字段完全提取',
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.failed,
|
||||
label: '提取字段缺失',
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.contextExtractFields,
|
||||
label: '完整提取结果',
|
||||
description: '一个 JSON 字符串,例如:{"name:":"YY","Time":"2023/7/2 18:00"}',
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
};
|
109
packages/global/core/module/template/system/datasetSearch.ts
Normal file
109
packages/global/core/module/template/system/datasetSearch.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import {
|
||||
ModuleDataTypeEnum,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum,
|
||||
ModuleTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import { Input_Template_TFSwitch, Input_Template_UserChatInput } from '../input';
|
||||
import { Output_Template_Finish } from '../output';
|
||||
|
||||
export const DatasetSearchModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.datasetSearchNode,
|
||||
templateType: ModuleTemplateTypeEnum.dataset,
|
||||
flowType: FlowNodeTypeEnum.datasetSearchNode,
|
||||
avatar: '/imgs/module/db.png',
|
||||
name: '知识库搜索',
|
||||
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.datasetSelectList,
|
||||
type: FlowNodeInputTypeEnum.selectDataset,
|
||||
label: '关联的知识库',
|
||||
value: [],
|
||||
valueType: ModuleDataTypeEnum.selectDataset,
|
||||
list: [],
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: true
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.datasetSimilarity,
|
||||
type: FlowNodeInputTypeEnum.slider,
|
||||
label: '最低相关性',
|
||||
value: 0.4,
|
||||
valueType: ModuleDataTypeEnum.number,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
markList: [
|
||||
{ label: '0', value: 0 },
|
||||
{ label: '1', value: 1 }
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.datasetLimit,
|
||||
type: FlowNodeInputTypeEnum.slider,
|
||||
label: '单次搜索上限',
|
||||
description: '最多取 n 条记录作为本次问题引用',
|
||||
value: 5,
|
||||
valueType: ModuleDataTypeEnum.number,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
markList: [
|
||||
{ label: '1', value: 1 },
|
||||
{ label: '20', value: 20 }
|
||||
],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.datasetStartReRank,
|
||||
type: FlowNodeInputTypeEnum.switch,
|
||||
label: '结果重排',
|
||||
description: '将召回的结果进行进一步重排,可增加召回率',
|
||||
plusField: true,
|
||||
value: false,
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
Input_Template_UserChatInput
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleOutputKeyEnum.datasetIsEmpty,
|
||||
label: '搜索结果为空',
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.datasetUnEmpty,
|
||||
label: '搜索结果不为空',
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.datasetQuoteQA,
|
||||
label: '引用内容',
|
||||
description:
|
||||
'始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器',
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
valueType: ModuleDataTypeEnum.datasetQuote,
|
||||
targets: []
|
||||
},
|
||||
Output_Template_Finish
|
||||
]
|
||||
};
|
14
packages/global/core/module/template/system/empty.ts
Normal file
14
packages/global/core/module/template/system/empty.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
|
||||
export const EmptyModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.empty,
|
||||
templateType: ModuleTemplateTypeEnum.other,
|
||||
flowType: FlowNodeTypeEnum.empty,
|
||||
avatar: '/imgs/module/cq.png',
|
||||
name: '该模块已被移除',
|
||||
intro: '',
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
46
packages/global/core/module/template/system/history.ts
Normal file
46
packages/global/core/module/template/system/history.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
|
||||
|
||||
export const HistoryModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.historyNode,
|
||||
templateType: ModuleTemplateTypeEnum.systemInput,
|
||||
flowType: FlowNodeTypeEnum.historyNode,
|
||||
avatar: '/imgs/module/history.png',
|
||||
name: '聊天记录',
|
||||
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
|
||||
inputs: [
|
||||
{
|
||||
key: ModuleInputKeyEnum.historyMaxAmount,
|
||||
type: FlowNodeInputTypeEnum.numberInput,
|
||||
label: '最长记录数',
|
||||
value: 6,
|
||||
valueType: ModuleDataTypeEnum.number,
|
||||
min: 0,
|
||||
max: 50,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.history,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
valueType: ModuleDataTypeEnum.chatHistory,
|
||||
label: '聊天记录',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleInputKeyEnum.history,
|
||||
label: '聊天记录',
|
||||
valueType: ModuleDataTypeEnum.chatHistory,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
};
|
31
packages/global/core/module/template/system/http.ts
Normal file
31
packages/global/core/module/template/system/http.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { Input_Template_TFSwitch } from '../input';
|
||||
import { Output_Template_Finish } from '../output';
|
||||
|
||||
export const HttpModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.httpRequest,
|
||||
templateType: ModuleTemplateTypeEnum.externalCall,
|
||||
flowType: FlowNodeTypeEnum.httpRequest,
|
||||
avatar: '/imgs/module/http.png',
|
||||
name: 'HTTP模块',
|
||||
intro: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.httpUrl,
|
||||
value: '',
|
||||
type: FlowNodeInputTypeEnum.input,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
label: '请求地址',
|
||||
description: '请求目标地址',
|
||||
placeholder: 'https://api.fastgpt.run/getInventory',
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: [Output_Template_Finish]
|
||||
};
|
15
packages/global/core/module/template/system/pluginInput.ts
Normal file
15
packages/global/core/module/template/system/pluginInput.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
|
||||
export const PluginInputModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.pluginInput,
|
||||
templateType: ModuleTemplateTypeEnum.systemInput,
|
||||
flowType: FlowNodeTypeEnum.pluginInput,
|
||||
avatar: '/imgs/module/input.png',
|
||||
name: '定义插件输入',
|
||||
intro: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入',
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
15
packages/global/core/module/template/system/pluginOutput.ts
Normal file
15
packages/global/core/module/template/system/pluginOutput.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
|
||||
export const PluginOutputModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.pluginOutput,
|
||||
templateType: ModuleTemplateTypeEnum.systemInput,
|
||||
flowType: FlowNodeTypeEnum.pluginOutput,
|
||||
avatar: '/imgs/module/output.png',
|
||||
name: '定义插件输出',
|
||||
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
62
packages/global/core/module/template/system/runApp.ts
Normal file
62
packages/global/core/module/template/system/runApp.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import {
|
||||
ModuleDataTypeEnum,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum,
|
||||
ModuleTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import {
|
||||
Input_Template_History,
|
||||
Input_Template_TFSwitch,
|
||||
Input_Template_UserChatInput
|
||||
} from '../input';
|
||||
import { Output_Template_Finish } from '../output';
|
||||
|
||||
export const RunAppModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.runApp,
|
||||
templateType: ModuleTemplateTypeEnum.externalCall,
|
||||
flowType: FlowNodeTypeEnum.runApp,
|
||||
avatar: '/imgs/module/app.png',
|
||||
name: '应用调用',
|
||||
intro: '可以选择一个其他应用进行调用',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: ModuleInputKeyEnum.runAppSelectApp,
|
||||
type: FlowNodeInputTypeEnum.selectApp,
|
||||
valueType: ModuleDataTypeEnum.selectApp,
|
||||
label: '选择一个应用',
|
||||
description: '选择一个其他应用进行调用',
|
||||
required: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleOutputKeyEnum.history,
|
||||
label: '新的上下文',
|
||||
description: '将该应用回复内容拼接到历史记录中,作为新的上下文返回',
|
||||
valueType: ModuleDataTypeEnum.chatHistory,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: ModuleOutputKeyEnum.answerText,
|
||||
label: 'AI回复',
|
||||
description: '将在应用完全结束后触发',
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
Output_Template_Finish
|
||||
]
|
||||
};
|
15
packages/global/core/module/template/system/runPlugin.ts
Normal file
15
packages/global/core/module/template/system/runPlugin.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ModuleTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
|
||||
export const RunPluginModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.pluginModule,
|
||||
templateType: ModuleTemplateTypeEnum.externalCall,
|
||||
flowType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: '/imgs/module/custom.png',
|
||||
intro: '',
|
||||
name: '自定义模块',
|
||||
showStatus: false,
|
||||
inputs: [], // [{key:'pluginId'},...]
|
||||
outputs: []
|
||||
};
|
49
packages/global/core/module/template/system/userGuide.ts
Normal file
49
packages/global/core/module/template/system/userGuide.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import { userGuideTip } from '../tip';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
|
||||
|
||||
export const UserGuideModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.userGuide,
|
||||
templateType: ModuleTemplateTypeEnum.userGuide,
|
||||
flowType: FlowNodeTypeEnum.userGuide,
|
||||
avatar: '/imgs/module/userGuide.png',
|
||||
name: '用户引导',
|
||||
intro: userGuideTip,
|
||||
inputs: [
|
||||
{
|
||||
key: ModuleInputKeyEnum.welcomeText,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
label: '开场白',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.variables,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
label: '对话框变量',
|
||||
value: [],
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.questionGuide,
|
||||
valueType: ModuleDataTypeEnum.boolean,
|
||||
type: FlowNodeInputTypeEnum.switch,
|
||||
label: '问题引导',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
},
|
||||
{
|
||||
key: ModuleInputKeyEnum.tts,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
valueType: ModuleDataTypeEnum.any,
|
||||
label: '语音播报',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: []
|
||||
};
|
40
packages/global/core/module/template/system/userInput.ts
Normal file
40
packages/global/core/module/template/system/userInput.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowModuleTemplateType } from '../../type.d';
|
||||
import {
|
||||
ModuleDataTypeEnum,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum,
|
||||
ModuleTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
|
||||
export const UserInputModule: FlowModuleTemplateType = {
|
||||
id: FlowNodeTypeEnum.questionInput,
|
||||
templateType: ModuleTemplateTypeEnum.systemInput,
|
||||
flowType: FlowNodeTypeEnum.questionInput,
|
||||
avatar: '/imgs/module/userChatInput.png',
|
||||
name: '用户问题(入口)',
|
||||
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
|
||||
inputs: [
|
||||
{
|
||||
key: ModuleInputKeyEnum.userChatInput,
|
||||
type: FlowNodeInputTypeEnum.systemInput,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
label: '用户问题',
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: ModuleOutputKeyEnum.userChatInput,
|
||||
label: '用户问题',
|
||||
type: FlowNodeOutputTypeEnum.source,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
};
|
7
packages/global/core/module/template/tip.ts
Normal file
7
packages/global/core/module/template/tip.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const chatNodeSystemPromptTip =
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}';
|
||||
export const userGuideTip = '可以在对话前设置引导语,设置全局变量,设置下一步指引';
|
||||
export const welcomeTextTip =
|
||||
'每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]: 用户点击后可以直接发送该问题';
|
||||
export const variableTip =
|
||||
'可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等';
|
32
packages/global/core/module/type.d.ts
vendored
32
packages/global/core/module/type.d.ts
vendored
@@ -1,13 +1,14 @@
|
||||
import { FlowNodeTypeEnum, FlowNodeValTypeEnum } from './node/constant';
|
||||
import { FlowNodeTypeEnum } from './node/constant';
|
||||
import { ModuleDataTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants';
|
||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
|
||||
|
||||
export type FlowModuleTemplateType = {
|
||||
id: string;
|
||||
templateType: `${ModuleTemplateTypeEnum}`;
|
||||
flowType: `${FlowNodeTypeEnum}`; // unique
|
||||
logo?: string;
|
||||
avatar?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
intro?: string;
|
||||
intro: string; // template list intro
|
||||
showStatus?: boolean; // chatting response step status
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
@@ -15,16 +16,17 @@ export type FlowModuleTemplateType = {
|
||||
export type FlowModuleItemType = FlowModuleTemplateType & {
|
||||
moduleId: string;
|
||||
};
|
||||
export type SystemModuleTemplateType = {
|
||||
export type moduleTemplateListType = {
|
||||
type: `${ModuleTemplateTypeEnum}`;
|
||||
label: string;
|
||||
list: FlowModuleTemplateType[];
|
||||
}[];
|
||||
|
||||
// store module type
|
||||
export type ModuleItemType = {
|
||||
name: string;
|
||||
logo?: string;
|
||||
intro?: string;
|
||||
description?: string;
|
||||
moduleId: string;
|
||||
position?: {
|
||||
x: number;
|
||||
@@ -37,6 +39,24 @@ export type ModuleItemType = {
|
||||
};
|
||||
|
||||
/* function type */
|
||||
// variable
|
||||
export type VariableItemType = {
|
||||
id: string;
|
||||
key: string;
|
||||
label: string;
|
||||
type: `${VariableInputEnum}`;
|
||||
required: boolean;
|
||||
maxLen: number;
|
||||
enums: { value: string }[];
|
||||
};
|
||||
// tts
|
||||
export type AppTTSConfigType = {
|
||||
type: 'none' | 'web' | 'model';
|
||||
model?: string;
|
||||
voice?: string;
|
||||
speed?: number;
|
||||
};
|
||||
|
||||
export type SelectAppItemType = {
|
||||
id: string;
|
||||
name: string;
|
||||
|
@@ -1,28 +1,54 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeSpecialInputKeyEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from './node/constant';
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from './node/constant';
|
||||
import { ModuleDataTypeEnum, ModuleInputKeyEnum } from './constants';
|
||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
|
||||
import { ModuleItemType } from './type';
|
||||
import { AppTTSConfigType, ModuleItemType, VariableItemType } from './type';
|
||||
|
||||
export const getGuideModule = (modules: ModuleItemType[]) =>
|
||||
modules.find((item) => item.flowType === FlowNodeTypeEnum.userGuide);
|
||||
|
||||
export const splitGuideModule = (guideModules?: ModuleItemType) => {
|
||||
const welcomeText: string =
|
||||
guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.welcomeText)?.value || '';
|
||||
|
||||
const variableModules: VariableItemType[] =
|
||||
guideModules?.inputs.find((item) => item.key === ModuleInputKeyEnum.variables)?.value || [];
|
||||
|
||||
const questionGuide: boolean =
|
||||
!!guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.questionGuide)?.value ||
|
||||
false;
|
||||
|
||||
const ttsConfig: AppTTSConfigType = guideModules?.inputs?.find(
|
||||
(item) => item.key === ModuleInputKeyEnum.tts
|
||||
)?.value || { type: 'web' };
|
||||
|
||||
export function getPluginTemplatePluginIdInput(pluginId: string) {
|
||||
return {
|
||||
key: FlowNodeSpecialInputKeyEnum.pluginId,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
label: 'pluginId',
|
||||
value: pluginId,
|
||||
connected: true
|
||||
welcomeText,
|
||||
variableModules,
|
||||
questionGuide,
|
||||
ttsConfig
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export function formatPluginIOModules(
|
||||
export function formatPluginToPreviewModule(
|
||||
pluginId: string,
|
||||
modules: ModuleItemType[]
|
||||
): {
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
} {
|
||||
function getPluginTemplatePluginIdInput(pluginId: string): FlowNodeInputItemType {
|
||||
return {
|
||||
key: ModuleInputKeyEnum.pluginId,
|
||||
type: FlowNodeInputTypeEnum.hidden,
|
||||
label: 'pluginId',
|
||||
value: pluginId,
|
||||
valueType: ModuleDataTypeEnum.string,
|
||||
connected: true,
|
||||
showTargetInApp: false,
|
||||
showTargetInPlugin: false
|
||||
};
|
||||
}
|
||||
|
||||
const pluginInput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginInput);
|
||||
const customOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput);
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { ModuleTemplateTypeEnum } from '../module/constants';
|
||||
import { ModuleItemType } from '../module/type';
|
||||
|
||||
export const defaultModules: ModuleItemType[] = [
|
||||
{
|
||||
moduleId: 'fph4s3',
|
||||
moduleId: 'custom-output',
|
||||
name: '自定义输出',
|
||||
flowType: 'pluginOutput',
|
||||
showStatus: false,
|
||||
@@ -14,7 +15,7 @@ export const defaultModules: ModuleItemType[] = [
|
||||
outputs: []
|
||||
},
|
||||
{
|
||||
moduleId: 'w09v30',
|
||||
moduleId: 'custom-input',
|
||||
name: '自定义输入',
|
||||
flowType: 'pluginInput',
|
||||
showStatus: false,
|
||||
@@ -26,3 +27,14 @@ export const defaultModules: ModuleItemType[] = [
|
||||
outputs: []
|
||||
}
|
||||
];
|
||||
|
||||
export enum PluginTypeEnum {
|
||||
personal = 'personal',
|
||||
community = 'community',
|
||||
commercial = 'commercial'
|
||||
}
|
||||
export const PluginType2TemplateTypeMap = {
|
||||
[PluginTypeEnum.personal]: ModuleTemplateTypeEnum.personalPlugin,
|
||||
[PluginTypeEnum.community]: ModuleTemplateTypeEnum.communityPlugin,
|
||||
[PluginTypeEnum.commercial]: ModuleTemplateTypeEnum.commercialPlugin
|
||||
};
|
||||
|
13
packages/global/core/plugin/type.d.ts
vendored
13
packages/global/core/plugin/type.d.ts
vendored
@@ -1,4 +1,6 @@
|
||||
import { ModuleTemplateTypeEnum } from 'core/module/constants';
|
||||
import type { ModuleItemType } from '../module/type.d';
|
||||
import { PluginTypeEnum } from './constants';
|
||||
|
||||
export type PluginItemSchema = {
|
||||
_id: string;
|
||||
@@ -11,3 +13,14 @@ export type PluginItemSchema = {
|
||||
updateTime: Date;
|
||||
modules: ModuleItemType[];
|
||||
};
|
||||
|
||||
/* plugin template */
|
||||
export type PluginTemplateType = {
|
||||
id: string;
|
||||
type: `${PluginTypeEnum}`;
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
modules: ModuleItemType[];
|
||||
templateType?: `${ModuleTemplateTypeEnum}`;
|
||||
};
|
||||
|
5
packages/global/support/user/api.d.ts
vendored
5
packages/global/support/user/api.d.ts
vendored
@@ -13,3 +13,8 @@ export type OauthLoginProps = {
|
||||
inviterId?: string;
|
||||
tmbId?: string;
|
||||
};
|
||||
|
||||
export type FastLoginProps = {
|
||||
token: string;
|
||||
code: string;
|
||||
};
|
||||
|
Reference in New Issue
Block a user