diff --git a/packages/global/common/string/textSplitter.ts b/packages/global/common/string/textSplitter.ts index ea9e6de51..6f0eadb7a 100644 --- a/packages/global/common/string/textSplitter.ts +++ b/packages/global/common/string/textSplitter.ts @@ -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++) { diff --git a/packages/global/core/app/api.d.ts b/packages/global/core/app/api.d.ts index 2fad2771c..ca0160a03 100644 --- a/packages/global/core/app/api.d.ts +++ b/packages/global/core/app/api.d.ts @@ -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[]; +}; diff --git a/packages/global/core/app/constants.ts b/packages/global/core/app/constants.ts index 066d31312..c2b2b5e7a 100644 --- a/packages/global/core/app/constants.ts +++ b/packages/global/core/app/constants.ts @@ -1,4 +1,12 @@ export enum AppTypeEnum { - basic = 'basic', + simple = 'simple', advanced = 'advanced' } +export const AppTypeMap = { + [AppTypeEnum.simple]: { + label: 'simple' + }, + [AppTypeEnum.advanced]: { + label: 'advanced' + } +}; diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index e61b8e5a4..40613ffc0 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -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; + }; + }; +}; diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts new file mode 100644 index 000000000..0e8d01465 --- /dev/null +++ b/packages/global/core/app/utils.ts @@ -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; +}; diff --git a/packages/global/core/chat/api.d.ts b/packages/global/core/chat/api.d.ts index 97844d5f6..9ec93d581 100644 --- a/packages/global/core/chat/api.d.ts +++ b/packages/global/core/chat/api.d.ts @@ -30,5 +30,4 @@ export type InitChatResponse = { export type ChatHistoryItemResType = moduleDispatchResType & { moduleType: `${FlowNodeTypeEnum}`; moduleName: string; - moduleLogo?: string; }; diff --git a/packages/global/core/chat/constants.ts b/packages/global/core/chat/constants.ts index 26e2ead32..8a9213a4c 100644 --- a/packages/global/core/chat/constants.ts +++ b/packages/global/core/chat/constants.ts @@ -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: '系统提示词' diff --git a/packages/global/core/chat/type.d.ts b/packages/global/core/chat/type.d.ts index 1999b7800..bcad0f51e 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -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[]; diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts new file mode 100644 index 000000000..b0502ac94 --- /dev/null +++ b/packages/global/core/module/constants.ts @@ -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' +} diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index 14e7abc48..4252322b9 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -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' -} diff --git a/packages/global/core/module/node/type.d.ts b/packages/global/core/module/node/type.d.ts index 9ee5ee4f9..8c0cc97b2 100644 --- a/packages/global/core/module/node/type.d.ts +++ b/packages/global/core/module/node/type.d.ts @@ -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; +}; diff --git a/packages/global/core/module/template/input.ts b/packages/global/core/module/template/input.ts new file mode 100644 index 000000000..37f6a8b34 --- /dev/null +++ b/packages/global/core/module/template/input.ts @@ -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 +}; diff --git a/packages/global/core/module/template/output.ts b/packages/global/core/module/template/output.ts new file mode 100644 index 000000000..864ad3d3d --- /dev/null +++ b/packages/global/core/module/template/output.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/aiChat.ts b/packages/global/core/module/template/system/aiChat.ts new file mode 100644 index 000000000..4867287af --- /dev/null +++ b/packages/global/core/module/template/system/aiChat.ts @@ -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 + ] +}; diff --git a/packages/global/core/module/template/system/assignedAnswer.ts b/packages/global/core/module/template/system/assignedAnswer.ts new file mode 100644 index 000000000..23c0e39cf --- /dev/null +++ b/packages/global/core/module/template/system/assignedAnswer.ts @@ -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] +}; diff --git a/packages/global/core/module/template/system/classifyQuestion.ts b/packages/global/core/module/template/system/classifyQuestion.ts new file mode 100644 index 000000000..d642eddc4 --- /dev/null +++ b/packages/global/core/module/template/system/classifyQuestion.ts @@ -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: [] + } + ] +}; diff --git a/packages/global/core/module/template/system/contextExtract.ts b/packages/global/core/module/template/system/contextExtract.ts new file mode 100644 index 000000000..24e7b4e50 --- /dev/null +++ b/packages/global/core/module/template/system/contextExtract.ts @@ -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: [] + } + ] +}; diff --git a/packages/global/core/module/template/system/datasetSearch.ts b/packages/global/core/module/template/system/datasetSearch.ts new file mode 100644 index 000000000..ffabb1e78 --- /dev/null +++ b/packages/global/core/module/template/system/datasetSearch.ts @@ -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 + ] +}; diff --git a/packages/global/core/module/template/system/empty.ts b/packages/global/core/module/template/system/empty.ts new file mode 100644 index 000000000..5f3089eb7 --- /dev/null +++ b/packages/global/core/module/template/system/empty.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/history.ts b/packages/global/core/module/template/system/history.ts new file mode 100644 index 000000000..fa4903058 --- /dev/null +++ b/packages/global/core/module/template/system/history.ts @@ -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: [] + } + ] +}; diff --git a/packages/global/core/module/template/system/http.ts b/packages/global/core/module/template/system/http.ts new file mode 100644 index 000000000..d736ae944 --- /dev/null +++ b/packages/global/core/module/template/system/http.ts @@ -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] +}; diff --git a/packages/global/core/module/template/system/pluginInput.ts b/packages/global/core/module/template/system/pluginInput.ts new file mode 100644 index 000000000..273c1f29c --- /dev/null +++ b/packages/global/core/module/template/system/pluginInput.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/pluginOutput.ts b/packages/global/core/module/template/system/pluginOutput.ts new file mode 100644 index 000000000..8017bb879 --- /dev/null +++ b/packages/global/core/module/template/system/pluginOutput.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/runApp.ts b/packages/global/core/module/template/system/runApp.ts new file mode 100644 index 000000000..b5fd99e8a --- /dev/null +++ b/packages/global/core/module/template/system/runApp.ts @@ -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 + ] +}; diff --git a/packages/global/core/module/template/system/runPlugin.ts b/packages/global/core/module/template/system/runPlugin.ts new file mode 100644 index 000000000..51b563b52 --- /dev/null +++ b/packages/global/core/module/template/system/runPlugin.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/userGuide.ts b/packages/global/core/module/template/system/userGuide.ts new file mode 100644 index 000000000..88a47faf9 --- /dev/null +++ b/packages/global/core/module/template/system/userGuide.ts @@ -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: [] +}; diff --git a/packages/global/core/module/template/system/userInput.ts b/packages/global/core/module/template/system/userInput.ts new file mode 100644 index 000000000..8c5989161 --- /dev/null +++ b/packages/global/core/module/template/system/userInput.ts @@ -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: [] + } + ] +}; diff --git a/packages/global/core/module/template/tip.ts b/packages/global/core/module/template/tip.ts new file mode 100644 index 000000000..e64ea65e2 --- /dev/null +++ b/packages/global/core/module/template/tip.ts @@ -0,0 +1,7 @@ +export const chatNodeSystemPromptTip = + '模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}'; +export const userGuideTip = '可以在对话前设置引导语,设置全局变量,设置下一步指引'; +export const welcomeTextTip = + '每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]: 用户点击后可以直接发送该问题'; +export const variableTip = + '可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等'; diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index f4c339618..15eced56f 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -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; diff --git a/packages/global/core/module/utils.ts b/packages/global/core/module/utils.ts index 5355676ec..07b91b37e 100644 --- a/packages/global/core/module/utils.ts +++ b/packages/global/core/module/utils.ts @@ -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); diff --git a/packages/global/core/plugin/constants.ts b/packages/global/core/plugin/constants.ts index f90764293..e7dd8e5dc 100644 --- a/packages/global/core/plugin/constants.ts +++ b/packages/global/core/plugin/constants.ts @@ -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 +}; diff --git a/packages/global/core/plugin/type.d.ts b/packages/global/core/plugin/type.d.ts index ebb3857ab..c17b3e59b 100644 --- a/packages/global/core/plugin/type.d.ts +++ b/packages/global/core/plugin/type.d.ts @@ -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}`; +}; diff --git a/packages/global/support/user/api.d.ts b/packages/global/support/user/api.d.ts index 1419fedf4..dd256039d 100644 --- a/packages/global/support/user/api.d.ts +++ b/packages/global/support/user/api.d.ts @@ -13,3 +13,8 @@ export type OauthLoginProps = { inviterId?: string; tmbId?: string; }; + +export type FastLoginProps = { + token: string; + code: string; +}; diff --git a/packages/service/core/app/schema.ts b/packages/service/core/app/schema.ts index 5240a9b70..551e487e0 100644 --- a/packages/service/core/app/schema.ts +++ b/packages/service/core/app/schema.ts @@ -1,3 +1,4 @@ +import { AppTypeMap } from '@fastgpt/global/core/app/constants'; import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; @@ -31,7 +32,11 @@ const AppSchema = new Schema({ type: { type: String, default: 'advanced', - enum: ['basic', 'advanced'] + enum: Object.keys(AppTypeMap) + }, + simpleTemplateId: { + type: String, + required: true }, avatar: { type: String, @@ -61,6 +66,7 @@ const AppSchema = new Schema({ try { AppSchema.index({ updateTime: -1 }); + AppSchema.index({ teamId: 1 }); } catch (error) { console.log(error); } diff --git a/packages/service/core/chat/chatItemSchema.ts b/packages/service/core/chat/chatItemSchema.ts index ea7a4f5fd..3459f0908 100644 --- a/packages/service/core/chat/chatItemSchema.ts +++ b/packages/service/core/chat/chatItemSchema.ts @@ -1,7 +1,7 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import { ChatItemSchema as ChatItemType } from '@fastgpt/global/core/chat/type'; -import { ChatRoleMap, TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; +import { ChatRoleMap } from '@fastgpt/global/core/chat/constants'; import { customAlphabet } from 'nanoid'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); import { @@ -10,6 +10,7 @@ import { } from '@fastgpt/global/support/user/team/constant'; import { appCollectionName } from '../app/schema'; import { userCollectionName } from '../../support/user/schema'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; const ChatItemSchema = new Schema({ dataId: { @@ -65,7 +66,7 @@ const ChatItemSchema = new Schema({ a: String } }, - [TaskResponseKeyEnum.responseData]: { + [ModuleOutputKeyEnum.responseData]: { type: Array, default: [] } diff --git a/packages/service/core/chat/chatSchema.ts b/packages/service/core/chat/chatSchema.ts index 66bacf8e7..33e154e59 100644 --- a/packages/service/core/chat/chatSchema.ts +++ b/packages/service/core/chat/chatSchema.ts @@ -1,16 +1,13 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d'; -import { - ChatRoleMap, - ChatSourceMap, - TaskResponseKeyEnum -} from '@fastgpt/global/core/chat/constants'; +import { ChatRoleMap, ChatSourceMap } from '@fastgpt/global/core/chat/constants'; import { TeamCollectionName, TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; import { appCollectionName } from '../app/schema'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; export const chatCollectionName = 'chat'; @@ -81,7 +78,7 @@ const ChatSchema = new Schema({ type: String, default: '' }, - [TaskResponseKeyEnum.responseData]: { + [ModuleOutputKeyEnum.responseData]: { type: Array, default: [] } diff --git a/packages/service/core/dataset/data/schema.ts b/packages/service/core/dataset/data/schema.ts index b51262478..a7a51adcc 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -43,6 +43,14 @@ const DatasetDataSchema = new Schema({ type: String, default: '' }, + qToken: { + type: String, + default: '' + }, + aToken: { + type: String, + default: '' + }, indexes: { type: [ { @@ -70,9 +78,11 @@ const DatasetDataSchema = new Schema({ }); try { - DatasetDataSchema.index({ userId: 1 }); + DatasetDataSchema.index({ teamId: 1 }); DatasetDataSchema.index({ datasetId: 1 }); DatasetDataSchema.index({ collectionId: 1 }); + // full text index + DatasetDataSchema.index({ qToken: 'text', aToken: 'text' }); } catch (error) { console.log(error); } diff --git a/packages/service/core/plugin/controller.ts b/packages/service/core/plugin/controller.ts index 64b8a8273..aae3f3476 100644 --- a/packages/service/core/plugin/controller.ts +++ b/packages/service/core/plugin/controller.ts @@ -1,40 +1,100 @@ import { MongoPlugin } from './schema'; import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; -import { formatPluginIOModules } from '@fastgpt/global/core/module/utils'; +import { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils'; +import { PluginType2TemplateTypeMap, PluginTypeEnum } from '@fastgpt/global/core/plugin/constants'; +import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d'; -/* plugin templates */ -export async function getUserPlugins2Templates({ - teamId -}: { - teamId: string; -}): Promise { - const plugins = await MongoPlugin.find({ teamId }).lean(); +/* + plugin id rule: + personal: id + community: community-id + commercial: commercial-id +*/ - return plugins.map((plugin) => ({ - id: String(plugin._id), - flowType: FlowNodeTypeEnum.pluginModule, - logo: plugin.avatar, - name: plugin.name, - description: plugin.intro, - intro: plugin.intro, - showStatus: false, - inputs: [], - outputs: [] - })); +export async function splitCombinePluginId(id: string) { + const splitRes = id.split('-'); + if (splitRes.length === 1 && id.length === 24) { + return { + type: PluginTypeEnum.personal, + pluginId: id + }; + } + + const [type, pluginId] = id.split('-') as [`${PluginTypeEnum}`, string]; + if (!type || !pluginId) return Promise.reject('pluginId not found'); + + return { type, pluginId: id }; } -/* one plugin 2 module detail */ -export async function getPluginModuleDetail({ id }: { id: string }) { - const plugin = await MongoPlugin.findById(id); +/* format plugin modules to plugin preview module */ +export async function getPluginPreviewModule({ + id +}: { + id: string; +}): Promise { + // classify + const { type, pluginId } = await splitCombinePluginId(id); + + const plugin = await (async () => { + if (type === PluginTypeEnum.community) { + return global.communityPlugins?.find((plugin) => plugin.id === pluginId); + } + if (type === PluginTypeEnum.personal) { + const item = await MongoPlugin.findById(id); + if (!item) return undefined; + return { + id: String(item._id), + name: item.name, + avatar: item.avatar, + intro: item.intro, + type: PluginTypeEnum.personal, + modules: item.modules + }; + } + })(); + if (!plugin) return Promise.reject('plugin not found'); return { - id: String(plugin._id), + id: plugin.id, + templateType: PluginType2TemplateTypeMap[plugin.type], flowType: FlowNodeTypeEnum.pluginModule, - logo: plugin.avatar, + avatar: plugin.avatar, name: plugin.name, - description: plugin.intro, intro: plugin.intro, - showStatus: false, - ...formatPluginIOModules(String(plugin._id), plugin.modules) + showStatus: true, + ...formatPluginToPreviewModule(plugin.id, plugin.modules) + }; +} + +export async function getPluginRuntimeById(id: string): Promise { + const { type, pluginId } = await splitCombinePluginId(id); + + const plugin = await (async () => { + if (type === PluginTypeEnum.community) { + return global.communityPlugins?.find((plugin) => plugin.id === pluginId); + } + if (type === PluginTypeEnum.personal) { + const item = await MongoPlugin.findById(id); + if (!item) return undefined; + return { + id: String(item._id), + name: item.name, + avatar: item.avatar, + intro: item.intro, + type: PluginTypeEnum.personal, + modules: item.modules + }; + } + })(); + + if (!plugin) return Promise.reject('plugin not found'); + + return { + id: plugin.id, + type: plugin.type, + name: plugin.name, + avatar: plugin.avatar, + intro: plugin.intro, + modules: plugin.modules }; } diff --git a/packages/service/core/plugin/schema.ts b/packages/service/core/plugin/schema.ts index ea933ff07..48f94a65e 100644 --- a/packages/service/core/plugin/schema.ts +++ b/packages/service/core/plugin/schema.ts @@ -6,7 +6,7 @@ import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; -export const ModuleCollectionName = 'plugins'; +export const PluginCollectionName = 'plugins'; const PluginSchema = new Schema({ userId: { @@ -52,5 +52,5 @@ try { } export const MongoPlugin: Model = - models[ModuleCollectionName] || model(ModuleCollectionName, PluginSchema); + models[PluginCollectionName] || model(PluginCollectionName, PluginSchema); MongoPlugin.syncIndexes(); diff --git a/packages/service/core/plugin/store/schema.ts b/packages/service/core/plugin/store/schema.ts new file mode 100644 index 000000000..2f11d33b8 --- /dev/null +++ b/packages/service/core/plugin/store/schema.ts @@ -0,0 +1,31 @@ +import { connectionMongo, type Model } from '../../../common/mongo'; +const { Schema, model, models } = connectionMongo; +import type { PluginItemSchema } from '@fastgpt/global/core/plugin/type.d'; + +import { PluginCollectionName } from '../schema'; + +export const ModuleCollectionName = 'plugins'; + +const PluginStoreSchema = new Schema({ + pluginId: { + type: Schema.Types.ObjectId, + ref: PluginCollectionName, + required: true + }, + price: { + type: Number, + default: 0 + }, + updateTime: { + type: Date, + default: () => new Date() + }, + modules: { + type: Array, + default: [] + } +}); + +export const MongoPluginStore: Model = + models[ModuleCollectionName] || model(ModuleCollectionName, PluginStoreSchema); +MongoPluginStore.syncIndexes(); diff --git a/packages/service/core/plugin/type.d.ts b/packages/service/core/plugin/type.d.ts new file mode 100644 index 000000000..9f092972b --- /dev/null +++ b/packages/service/core/plugin/type.d.ts @@ -0,0 +1,5 @@ +import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d'; + +declare global { + var communityPlugins: PluginTemplateType[]; +} diff --git a/packages/service/support/permission/auth/chat.ts b/packages/service/support/permission/auth/chat.ts index c0a89042e..db89a4add 100644 --- a/packages/service/support/permission/auth/chat.ts +++ b/packages/service/support/permission/auth/chat.ts @@ -1,6 +1,6 @@ import { AuthResponseType } from '@fastgpt/global/support/permission/type'; import { AuthModeType } from '../type'; -import type { ChatSchema, ChatWithAppSchema } from '@fastgpt/global/core/chat/type'; +import type { ChatWithAppSchema } from '@fastgpt/global/core/chat/type'; import { parseHeaderCert } from '../controller'; import { MongoChat } from '../../../core/chat/chatSchema'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; @@ -16,7 +16,7 @@ export async function authChat({ chatId: string; }): Promise< AuthResponseType & { - chat: ChatSchema; + chat: ChatWithAppSchema; } > { const { userId, teamId, tmbId } = await parseHeaderCert(props); diff --git a/packages/service/support/permission/auth/dataset.ts b/packages/service/support/permission/auth/dataset.ts index 84bca9474..8b867df1f 100644 --- a/packages/service/support/permission/auth/dataset.ts +++ b/packages/service/support/permission/auth/dataset.ts @@ -15,19 +15,17 @@ import { getFileById } from '../../../common/file/gridfs/controller'; import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import { getTeamInfoByTmbId } from '../../user/team/controller'; -export async function authDataset({ +export async function authDatasetByTmbId({ + teamId, + tmbId, datasetId, - per = 'owner', - ...props -}: AuthModeType & { + per +}: { + teamId: string; + tmbId: string; datasetId: string; -}): Promise< - AuthResponseType & { - dataset: DatasetSchemaType; - } -> { - const result = await parseHeaderCert(props); - const { teamId, tmbId } = result; + per: AuthModeType['per']; +}) { const { role } = await getTeamInfoByTmbId({ tmbId }); const { dataset, isOwner, canWrite } = await (async () => { @@ -58,6 +56,32 @@ export async function authDataset({ return { dataset, isOwner, canWrite }; })(); + return { + dataset, + isOwner, + canWrite + }; +} +export async function authDataset({ + datasetId, + per = 'owner', + ...props +}: AuthModeType & { + datasetId: string; +}): Promise< + AuthResponseType & { + dataset: DatasetSchemaType; + } +> { + const result = await parseHeaderCert(props); + const { teamId, tmbId } = result; + const { dataset, isOwner, canWrite } = await authDatasetByTmbId({ + teamId, + tmbId, + datasetId, + per + }); + return { ...result, dataset, diff --git a/packages/service/support/permission/auth/plugin.ts b/packages/service/support/permission/auth/plugin.ts index 5053ad4d1..98aecc3b7 100644 --- a/packages/service/support/permission/auth/plugin.ts +++ b/packages/service/support/permission/auth/plugin.ts @@ -6,6 +6,8 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { MongoPlugin } from '../../../core/plugin/schema'; import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin'; import { PluginItemSchema } from '@fastgpt/global/core/plugin/type'; +import { splitCombinePluginId } from '../../../core/plugin/controller'; +import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants'; export async function authPluginCrud({ id, @@ -54,3 +56,29 @@ export async function authPluginCrud({ canWrite }; } + +export async function authPluginCanUse({ + id, + teamId, + tmbId +}: { + id: string; + teamId: string; + tmbId: string; +}) { + const { type, pluginId } = await splitCombinePluginId(id); + + if (type === PluginTypeEnum.community) { + return true; + } + + if (type === PluginTypeEnum.personal) { + const { role } = await getTeamInfoByTmbId({ tmbId }); + const plugin = await MongoPlugin.findOne({ _id: pluginId, teamId }); + if (!plugin) { + return Promise.reject(PluginErrEnum.unExist); + } + } + + return true; +} diff --git a/projects/app/public/imgs/modal/badAnswer.svg b/projects/app/public/imgs/modal/badAnswer.svg new file mode 100644 index 000000000..6d3ee6122 --- /dev/null +++ b/projects/app/public/imgs/modal/badAnswer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/bill.svg b/projects/app/public/imgs/modal/bill.svg new file mode 100644 index 000000000..280d95bf9 --- /dev/null +++ b/projects/app/public/imgs/modal/bill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/chatHistory.svg b/projects/app/public/imgs/modal/chatHistory.svg new file mode 100644 index 000000000..cfbefbef9 --- /dev/null +++ b/projects/app/public/imgs/modal/chatHistory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/concat.svg b/projects/app/public/imgs/modal/concat.svg new file mode 100644 index 000000000..f721d5fdb --- /dev/null +++ b/projects/app/public/imgs/modal/concat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/edit.svg b/projects/app/public/imgs/modal/edit.svg new file mode 100644 index 000000000..a8fad3984 --- /dev/null +++ b/projects/app/public/imgs/modal/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/folder.svg b/projects/app/public/imgs/modal/folder.svg new file mode 100644 index 000000000..78d213c83 --- /dev/null +++ b/projects/app/public/imgs/modal/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/import.svg b/projects/app/public/imgs/modal/import.svg new file mode 100644 index 000000000..f1f356d5f --- /dev/null +++ b/projects/app/public/imgs/modal/import.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/key.svg b/projects/app/public/imgs/modal/key.svg new file mode 100644 index 000000000..96a8fe127 --- /dev/null +++ b/projects/app/public/imgs/modal/key.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/move.svg b/projects/app/public/imgs/modal/move.svg new file mode 100644 index 000000000..97cc5d826 --- /dev/null +++ b/projects/app/public/imgs/modal/move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/network.svg b/projects/app/public/imgs/modal/network.svg new file mode 100644 index 000000000..fabb15756 --- /dev/null +++ b/projects/app/public/imgs/modal/network.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/openai.svg b/projects/app/public/imgs/modal/openai.svg new file mode 100644 index 000000000..ac3776f00 --- /dev/null +++ b/projects/app/public/imgs/modal/openai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/params.svg b/projects/app/public/imgs/modal/params.svg new file mode 100644 index 000000000..9cc9bc37f --- /dev/null +++ b/projects/app/public/imgs/modal/params.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/password.svg b/projects/app/public/imgs/modal/password.svg new file mode 100644 index 000000000..8c8e0a428 --- /dev/null +++ b/projects/app/public/imgs/modal/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/pay.svg b/projects/app/public/imgs/modal/pay.svg new file mode 100644 index 000000000..f9a8f8fd6 --- /dev/null +++ b/projects/app/public/imgs/modal/pay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/preview.svg b/projects/app/public/imgs/modal/preview.svg new file mode 100644 index 000000000..4a8f3916e --- /dev/null +++ b/projects/app/public/imgs/modal/preview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/prompt.svg b/projects/app/public/imgs/modal/prompt.svg new file mode 100644 index 000000000..538c40d83 --- /dev/null +++ b/projects/app/public/imgs/modal/prompt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/quote.svg b/projects/app/public/imgs/modal/quote.svg new file mode 100644 index 000000000..38050a37a --- /dev/null +++ b/projects/app/public/imgs/modal/quote.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/readFeedback.svg b/projects/app/public/imgs/modal/readFeedback.svg new file mode 100644 index 000000000..d04633638 --- /dev/null +++ b/projects/app/public/imgs/modal/readFeedback.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/shareLight.svg b/projects/app/public/imgs/modal/shareLight.svg new file mode 100644 index 000000000..04b18a985 --- /dev/null +++ b/projects/app/public/imgs/modal/shareLight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/team.svg b/projects/app/public/imgs/modal/team.svg new file mode 100644 index 000000000..45a8f6507 --- /dev/null +++ b/projects/app/public/imgs/modal/team.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/txt.svg b/projects/app/public/imgs/modal/txt.svg new file mode 100644 index 000000000..a5fae29b8 --- /dev/null +++ b/projects/app/public/imgs/modal/txt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/modal/wholeRecord.svg b/projects/app/public/imgs/modal/wholeRecord.svg new file mode 100644 index 000000000..eb91469b0 --- /dev/null +++ b/projects/app/public/imgs/modal/wholeRecord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/module/templates.png b/projects/app/public/imgs/module/templates.png new file mode 100644 index 000000000..b6f7e49b8 Binary files /dev/null and b/projects/app/public/imgs/module/templates.png differ diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index ee93bc5e8..4c6249023 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -31,9 +31,8 @@ "Export Config Successful": "The configuration has been copied. Please check for important data", "Export Configs": "Export Configs", "Feedback Count": "User Feedback", - "Import Config": "Import Config", - "Import Config Failed": "Failed to import the configuration, please ensure that the configuration is normal!", "Import Configs": "Import Configs", + "Import Configs Failed": "Failed to import the configuration, please ensure that the configuration is normal!", "Input Field Settings": "Input Field Settings", "Logs Empty": "Logs is empty", "Logs Message Total": "Message Count", @@ -54,15 +53,7 @@ "My Modules": "My Custom Modules", "No Modules": "No module", "System Module": "System Module", - "type": "{{type}}\n{{example}}", - "valueType": { - "any": "any", - "boolean": "boolean", - "chatHistory": "Chat History", - "datasetQuote": "Dataset Quote", - "number": "number", - "string": "string" - } + "type": "{{type}}\n{{example}}" }, "modules": { "Title is required": "Title is required" @@ -115,9 +106,11 @@ "module model": "Model", "module name": "Name", "module price": "Price", + "module query": "Question/Query", "module question": "Question", "module quoteList": "Quotes", "module runningTime": "Time", + "module search query": "Query", "module search response": "Search Result", "module similarity": "Similarity", "module temperature": "Temperature", @@ -129,12 +122,14 @@ }, "common": { "Add": "Add", + "Add New": "Add", "Back": "Back", "Beta": "Beta", "Business edition features": "This is the commercial version function ~", "Choose": "Choose", "Close": "Close", "Collect": "Collect", + "Confirm": "Confirm", "Confirm Create": "Create", "Confirm Move": "Move here", "Confirm Update": "Update", @@ -174,6 +169,7 @@ "Rename Failed": "Rename Failed", "Rename Success": "Rename Success", "Request Error": "Request Error", + "Require Input": "Required", "Save Failed": "Save Failed", "Save Success": "Save Success", "Search": "Search", @@ -222,6 +218,11 @@ "Select TTS": "Select TTS", "TTS": "Audio Speech", "TTS Tip": "After this function is enabled, the voice playback function can be used after each conversation. Use of this feature may incur additional charges.", + "create app": "Create App", + "setting": "App Setting", + "simple": { + "mode template select": "Template" + }, "tts": { "Close": "NoUse", "Model alloy": "Female - Alloy", @@ -249,6 +250,7 @@ }, "dataset": { "Choose Dataset": "Choose Dataset", + "Create dataset": "Create Dataset", "Dataset": "Dataset", "Go Dataset": "To Dataset", "Quote Length": "Quote Length", @@ -260,6 +262,65 @@ "Edit": "Edit Data", "id": "Data ID" } + }, + "module": { + "Data Type": "Data Type", + "Field Description": "Description", + "Field Name": "Name", + "Field Type": "Type", + "Field key": "Key", + "Input Type": "Input Type", + "Plugin output must connect": "Custom outputs must all be connected", + "Variable": "Variable", + "Variable Setting": "Variable Setting", + "input": { + "label": { + "chat history": "", + "switch": "", + "user question": "" + } + }, + "inputType": { + "input": "Input", + "selectApp": "App Selector", + "selectChatModel": "Select Chat Model", + "selectDataset": "Dataset Selector", + "switch": "Switch", + "target": "Target Data", + "textarea": "Textarea" + }, + "output": { + "description": { + "running done": "running done" + }, + "label": { + "running done": "running done" + } + }, + "valueType": { + "any": "Any", + "boolean": "Boolean", + "chatHistory": "History", + "datasetQuote": "Dataset Quote", + "number": "Number", + "selectApp": "Select App", + "selectDataset": "Select Dataset", + "string": "String" + }, + "variable": { + "add option": "Add Option", + "key": "Key", + "key is required": "variable key is required", + "select type": "Select", + "text max length": "Max Length", + "text type": "Text", + "variable key is required": "", + "variable name": "Name", + "variable name is required": "variable name is required", + "variable option is required": "Variable option is required", + "variable option is value is required": "Variable option is value is required", + "variable options": "Options" + } } }, "dataset": { diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 29f9e21a6..c6d1e6cb7 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -16,7 +16,7 @@ "app": { "AI Advanced Settings": "AI 高级配置", "AI Settings": "AI 配置", - "Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", + "Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", "App Detail": "应用详情", "Basic Settings": "基本信息", "Chat Debug": "调试预览", @@ -31,9 +31,8 @@ "Export Config Successful": "已复制配置,请注意检查是否有重要数据", "Export Configs": "导出配置", "Feedback Count": "用户反馈", - "Import Config": "导入配置", - "Import Config Failed": "导入配置失败,请确保配置正常!", "Import Configs": "导入配置", + "Import Configs Failed": "导入配置失败,请确保配置正常!", "Input Field Settings": "输入字段编辑", "Logs Empty": "还没有日志噢~", "Logs Message Total": "消息总数", @@ -54,15 +53,7 @@ "My Modules": "", "No Modules": "还没有模块~", "System Module": "系统模块", - "type": "\"{{type}}\"类型\n{{example}}", - "valueType": { - "any": "任意", - "boolean": "布尔", - "chatHistory": "聊天记录", - "datasetQuote": "引用内容", - "number": "数字", - "string": "字符串" - } + "type": "\"{{type}}\"类型\n{{example}}" }, "modules": { "Title is required": "模块名不能为空" @@ -115,9 +106,11 @@ "module model": "模型", "module name": "模型名", "module price": "计费", + "module query": "问题/检索词", "module question": "问题", "module quoteList": "引用内容", "module runningTime": "运行时长", + "module search query": "检索词", "module search response": "搜索结果", "module similarity": "相似度", "module temperature": "温度", @@ -129,12 +122,14 @@ }, "common": { "Add": "添加", + "Add New": "新增", "Back": "返回", "Beta": "实验版", "Business edition features": "这是商业版功能~", "Choose": "选择", "Close": "关闭", "Collect": "收藏", + "Confirm": "确认", "Confirm Create": "确认创建", "Confirm Move": "移动到这", "Confirm Update": "确认更新", @@ -174,6 +169,7 @@ "Rename Failed": "重命名失败", "Rename Success": "重命名成功", "Request Error": "请求异常", + "Require Input": "必填", "Save Failed": "保存失败", "Save Success": "保存成功", "Search": "搜索", @@ -222,6 +218,11 @@ "Select TTS": "选择语音播放模式", "TTS": "语音播报", "TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。", + "create app": "创建属于你的 AI 应用", + "setting": "应用信息设置", + "simple": { + "mode template select": "简易模板" + }, "tts": { "Close": "不使用", "Model alloy": "女声 - Alloy", @@ -249,6 +250,7 @@ }, "dataset": { "Choose Dataset": "关联知识库", + "Create dataset": "创建一个知识库", "Dataset": "知识库", "Go Dataset": "前往知识库", "Quote Length": "引用内容长度", @@ -260,6 +262,65 @@ "Edit": "编辑数据", "id": "数据ID" } + }, + "module": { + "Data Type": "数据类型", + "Field Description": "字段描述", + "Field Name": "字段名", + "Field Type": "字段类型", + "Field key": "字段 Key", + "Input Type": "输入类型", + "Plugin output must connect": "自定义输出必须全部连接", + "Variable": "参数变量", + "Variable Setting": "变量设置", + "input": { + "label": { + "chat history": "聊天记录", + "switch": "触发器", + "user question": "用户问题" + } + }, + "inputType": { + "input": "输入框", + "selectApp": "应用选择", + "selectChatModel": "对话模型选择", + "selectDataset": "知识库选择", + "switch": "开关", + "target": "外部数据", + "textarea": "段落输入" + }, + "output": { + "description": { + "running done": "模块调用结束时触发" + }, + "label": { + "running done": "模块调用结束" + } + }, + "valueType": { + "any": "任意", + "boolean": "布尔", + "chatHistory": "聊天记录", + "datasetQuote": "引用内容", + "number": "数字", + "selectApp": "应用选择", + "selectDataset": "知识库选择", + "string": "字符串" + }, + "variable": { + "add option": "添加选项", + "key": "变量 key", + "key is required": "", + "select type": "下拉单选", + "text max length": "最大长度", + "text type": "文本", + "variable key is required": "变量 key 不能为空", + "variable name": "变量名", + "variable name is required": "变量名不能为空", + "variable option is required": "选项不能全空", + "variable option is value is required": "选项内容不能为空", + "variable options": "选项" + } } }, "dataset": { diff --git a/projects/app/public/simpleTemplates/fastgpt-simple.json b/projects/app/public/simpleTemplates/fastgpt-simple.json new file mode 100644 index 000000000..813705f15 --- /dev/null +++ b/projects/app/public/simpleTemplates/fastgpt-simple.json @@ -0,0 +1,27 @@ +{ + "name": "极简模板", + "desc": "极简模板\n已内置参数细节", + "systemForm": { + "aiSettings": { + "model": true, + "systemPrompt": true, + "temperature": false, + "maxToken": false, + "quoteTemplate": false, + "quotePrompt": false + }, + "dataset": { + "datasets": true, + "similarity": false, + "limit": false, + "rerank": false, + "searchEmptyText": false + }, + "userGuide": { + "welcomeText": true, + "variables": false, + "questionGuide": false, + "tts": true + } + } +} diff --git a/projects/app/src/components/Avatar/index.tsx b/projects/app/src/components/Avatar/index.tsx index a849e940d..ed70d20cf 100644 --- a/projects/app/src/components/Avatar/index.tsx +++ b/projects/app/src/components/Avatar/index.tsx @@ -3,7 +3,7 @@ import { Image } from '@chakra-ui/react'; import type { ImageProps } from '@chakra-ui/react'; import { LOGO_ICON } from '@fastgpt/global/core/chat/constants'; -const Avatar = ({ w = '30px', ...props }: ImageProps) => { +const Avatar = ({ w = '30px', src, ...props }: ImageProps) => { return ( { w={w} h={w} p={'1px'} + src={src || LOGO_ICON} {...props} /> ); diff --git a/projects/app/src/components/ChatBox/ContextModal.tsx b/projects/app/src/components/ChatBox/ContextModal.tsx index 1dd8d31e3..1ad96c2ac 100644 --- a/projects/app/src/components/ChatBox/ContextModal.tsx +++ b/projects/app/src/components/ChatBox/ContextModal.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ModalBody, Box, useTheme } from '@chakra-ui/react'; +import { ModalBody, Box, useTheme, Flex, Image } from '@chakra-ui/react'; import { ChatItemType } from '@fastgpt/global/core/chat/type'; import MyModal from '../MyModal'; @@ -16,13 +16,13 @@ const ContextModal = ({ + - - + {selectSimpleTemplate.systemForm.aiSettings?.systemPrompt && ( + + + {t('core.ai.Prompt')} + + + + + + + )} + + )} {/* dataset */} - - - - - {t('core.dataset.Choose Dataset')} - - - - {t('common.Choose')} - - - - {t('common.Params')} - - - - {t('core.dataset.Similarity')}: {getValues('dataset.searchSimilarity')},{' '} - {t('core.dataset.Search Top K')}: {getValues('dataset.searchLimit')} - {getValues('dataset.searchEmptyText') === '' - ? '' - : t('core.dataset.Set Empty Result Tip')} - - - {selectDatasets.map((item) => ( - - - router.push({ - pathname: '/dataset/detail', - query: { - datasetId: item._id - } - }) - } - > - - - {item.name} - + {selectSimpleTemplate?.systemForm?.dataset && ( + + + + {''} + {t('core.dataset.Choose Dataset')} + + {selectSimpleTemplate.systemForm.dataset.datasets && ( + + + {t('common.Choose')} - - ))} - - + )} + {(selectSimpleTemplate.systemForm.dataset.limit || + selectSimpleTemplate.systemForm.dataset.rerank || + selectSimpleTemplate.systemForm.dataset.searchEmptyText || + selectSimpleTemplate.systemForm.dataset.similarity) && ( + + + {t('common.Params')} + + )} + + + {t('core.dataset.Similarity')}: {getValues('dataset.similarity')},{' '} + {t('core.dataset.Search Top K')}: {getValues('dataset.limit')} + {getValues('dataset.searchEmptyText') === '' + ? '' + : t('core.dataset.Set Empty Result Tip')} + + + {selectDatasets.map((item) => ( + + + router.push({ + pathname: '/dataset/detail', + query: { + datasetId: item._id + } + }) + } + > + {''} + + {item.name} + + + + ))} + + + )} {/* tts */} - - { - setValue('tts', e); - setRefresh((state) => !state); - }} - /> - + {selectSimpleTemplate?.systemForm?.userGuide?.tts && ( + + { + setValue('userGuide.tts', e); + setRefresh((state) => !state); + }} + /> + + )} - {/* whisper */} - - { - const value = e.target.checked; - setValue('questionGuide', value); - setRefresh((state) => !state); - }} - /> - + {/* question guide */} + {selectSimpleTemplate?.systemForm?.userGuide?.questionGuide && ( + + { + const value = e.target.checked; + setValue('userGuide.questionGuide', value); + setRefresh((state) => !state); + }} + /> + + )} - {editVariable && ( - setEditVariable(undefined)} - onSubmit={({ variable }) => { - const record = variables.find((item) => item.id === variable.id); - if (record) { - replaceVariables( - variables.map((item) => (item.id === variable.id ? variable : item)) - ); - } else { - // auth same key - if (variables.find((item) => item.key === variable.key)) { - return toast({ - status: 'warning', - title: t('app.Variable Key Repeat Tip') - }); - } - appendVariable(variable); - } - - setEditVariable(undefined); - }} - /> - )} {isOpenAIChatSetting && ( { - setValue('chatModel', e); + setValue('aiSettings', e); onCloseAIChatSetting(); }} - defaultData={getValues('chatModel')} + defaultData={getValues('aiSettings')} + simpleModeTemplate={selectSimpleTemplate} /> )} {isOpenDatasetSelect && ( ({ + defaultSelectedDatasets={selectDatasets.map((item) => ({ datasetId: item._id, vectorModel: item.vectorModel }))} @@ -689,9 +661,8 @@ function ChatTest({ appId }: { appId: string }) { }, []); useEffect(() => { - const formVal = appModules2Form(appDetail.modules); - setModules(appForm2Modules(formVal)); resetChatBox(); + setModules(appDetail.modules); }, [appDetail, resetChatBox]); return ( @@ -727,7 +698,7 @@ function ChatTest({ appId }: { appId: string }) { onDelMessage={() => {}} /> - {appDetail.type !== AppTypeEnum.basic && ( + {appDetail.type !== AppTypeEnum.simple && ( { +const SimpleEdit = ({ appId }: { appId: string }) => { const { isPc } = useSystemStore(); return ( @@ -760,4 +731,4 @@ const BasicEdit = ({ appId }: { appId: string }) => { ); }; -export default BasicEdit; +export default SimpleEdit; diff --git a/projects/app/src/pages/app/detail/index.tsx b/projects/app/src/pages/app/detail/index.tsx index bd1703c62..90c08ad2d 100644 --- a/projects/app/src/pages/app/detail/index.tsx +++ b/projects/app/src/pages/app/detail/index.tsx @@ -12,7 +12,7 @@ import Avatar from '@/components/Avatar'; import MyIcon from '@/components/Icon'; import PageContainer from '@/components/PageContainer'; import Loading from '@/components/Loading'; -import BasicEdit from './components/BasicEdit'; +import SimpleEdit from './components/SimpleEdit'; import { serviceSideProps } from '@/web/common/utils/i18n'; import { useAppStore } from '@/web/core/app/store/useAppStore'; @@ -23,7 +23,7 @@ const OutLink = dynamic(() => import('./components/OutLink'), {}); const Logs = dynamic(() => import('./components/Logs'), {}); enum TabEnum { - 'basicEdit' = 'basicEdit', + 'simpleEdit' = 'simpleEdit', 'adEdit' = 'adEdit', 'outLink' = 'outLink', 'logs' = 'logs', @@ -51,7 +51,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => { const tabList = useMemo( () => [ - { label: '简易配置', id: TabEnum.basicEdit, icon: 'overviewLight' }, + { label: '简易配置', id: TabEnum.simpleEdit, icon: 'overviewLight' }, ...(feConfigs?.hide_app_flow ? [] : [{ label: '高级编排', id: TabEnum.adEdit, icon: 'settingLight' }]), @@ -167,9 +167,9 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => { /> - {currentTab === TabEnum.basicEdit && } + {currentTab === TabEnum.simpleEdit && } {currentTab === TabEnum.adEdit && appDetail && ( - setCurrentTab(TabEnum.basicEdit)} /> + setCurrentTab(TabEnum.simpleEdit)} /> )} {currentTab === TabEnum.logs && } {currentTab === TabEnum.outLink && } @@ -180,7 +180,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => { }; export async function getServerSideProps(context: any) { - const currentTab = context?.query?.currentTab || TabEnum.basicEdit; + const currentTab = context?.query?.currentTab || TabEnum.simpleEdit; return { props: { currentTab, ...(await serviceSideProps(context)) } diff --git a/projects/app/src/pages/app/list/component/CreateModal.tsx b/projects/app/src/pages/app/list/component/CreateModal.tsx index a34573e2d..80bb70a80 100644 --- a/projects/app/src/pages/app/list/component/CreateModal.tsx +++ b/projects/app/src/pages/app/list/component/CreateModal.tsx @@ -18,13 +18,14 @@ import { getErrText } from '@fastgpt/global/common/error/utils'; import { useToast } from '@/web/common/hooks/useToast'; import { postCreateApp } from '@/web/core/app/api'; import { useRouter } from 'next/router'; -import { appTemplates } from '@/constants/flow/ModuleTemplate'; +import { appTemplates } from '@/web/core/app/templates'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useRequest } from '@/web/common/hooks/useRequest'; import { feConfigs } from '@/web/common/system/staticData'; import Avatar from '@/components/Avatar'; import MyTooltip from '@/components/MyTooltip'; import MyModal from '@/components/MyModal'; +import { useTranslation } from 'next-i18next'; type FormType = { avatar: string; @@ -33,6 +34,7 @@ type FormType = { }; const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: () => void }) => { + const { t } = useTranslation(); const [refresh, setRefresh] = useState(false); const { toast } = useToast(); const router = useRouter(); @@ -96,8 +98,13 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: ( }); return ( - - 创建属于你的 AI 应用 + 取个响亮的名字 diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 4c3c57f90..2bcaea6bf 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -189,6 +189,14 @@ const MyApps = () => { ))} + {myApps.length === 0 && ( + + + + 还没有应用,快去创建一个吧! + + + )} {isOpenCreateModal && ( loadMyApps(true)} /> diff --git a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx index 6c87b59e5..4e546978b 100644 --- a/projects/app/src/pages/chat/components/ChatHistorySlider.tsx +++ b/projects/app/src/pages/chat/components/ChatHistorySlider.tsx @@ -254,7 +254,7 @@ const ChatHistorySlider = ({ }} > - 自定义标题 + {t('common.Custom Title')} )} + + 文件名 diff --git a/projects/app/src/pages/dataset/detail/components/Import/ImportModal.tsx b/projects/app/src/pages/dataset/detail/components/Import/ImportModal.tsx index 2da84e009..d39da93b5 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/ImportModal.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/ImportModal.tsx @@ -71,6 +71,7 @@ const ImportData = ({ return ( {t('dataset.data.File import')}} isOpen isCentered @@ -79,7 +80,7 @@ const ImportData = ({ h={'90vh'} > - + + {t('file.Fetch Url')} 目前仅支持读取静态链接,请注意检查结果 - + } top={'15vh'} isOpen diff --git a/projects/app/src/pages/dataset/list/component/CreateModal.tsx b/projects/app/src/pages/dataset/list/component/CreateModal.tsx index 12da57e55..d653ba6c6 100644 --- a/projects/app/src/pages/dataset/list/component/CreateModal.tsx +++ b/projects/app/src/pages/dataset/list/component/CreateModal.tsx @@ -1,5 +1,14 @@ import React, { useCallback, useState, useRef } from 'react'; -import { Box, Flex, Button, ModalHeader, ModalFooter, ModalBody, Input } from '@chakra-ui/react'; +import { + Box, + Flex, + Button, + ModalHeader, + ModalFooter, + ModalBody, + Input, + Image +} from '@chakra-ui/react'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useForm } from 'react-hook-form'; import { compressImgAndUpload } from '@/web/common/file/controller'; @@ -79,8 +88,14 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st }); return ( - - 创建一个知识库 + 取个名字 diff --git a/projects/app/src/pages/dataset/list/component/MoveModal.tsx b/projects/app/src/pages/dataset/list/component/MoveModal.tsx index 78a37355c..f0c71d9fa 100644 --- a/projects/app/src/pages/dataset/list/component/MoveModal.tsx +++ b/projects/app/src/pages/dataset/list/component/MoveModal.tsx @@ -59,9 +59,13 @@ const MoveModal = ({ }); return ( - - - + {!!parentId ? ( {paths.map((item, i) => ( @@ -93,8 +97,11 @@ const MoveModal = ({ ) : ( 我的知识库 )} - - + + } + onClose={onClose} + > + { + const { setLastChatId, setLastChatAppId } = useChatStore(); + const { setUserInfo } = useUserStore(); + const router = useRouter(); + const { toast } = useToast(); + + const loginSuccess = useCallback( + (res: ResLogin) => { + setToken(res.token); + setUserInfo(res.user); + + // init store + setLastChatId(''); + setLastChatAppId(''); + + setTimeout(() => { + router.push(decodeURIComponent(callbackUrl)); + }, 100); + }, + [setLastChatId, setLastChatAppId, setUserInfo, router, callbackUrl] + ); + + const authCode = useCallback( + async (code: string, token: string) => { + try { + const res = await postFastLogin({ + code, + token + }); + if (!res) { + toast({ + status: 'warning', + title: '登录异常' + }); + return setTimeout(() => { + router.replace('/login'); + }, 1000); + } + loginSuccess(res); + } catch (error) { + toast({ + status: 'warning', + title: getErrText(error, '登录异常') + }); + setTimeout(() => { + router.replace('/login'); + }, 1000); + } + }, + [loginSuccess, router, toast] + ); + + useEffect(() => { + clearToken(); + router.prefetch(callbackUrl); + authCode(code, token); + }, []); + + return ; +}; + +export async function getServerSideProps(content: any) { + return { + props: { + code: content?.query?.code || '', + token: content?.query?.token || '', + callbackUrl: content?.query?.callbackUrl || '/app/list', + ...(await serviceSideProps(content)) + } + }; +} + +export default FastLogin; diff --git a/projects/app/src/pages/login/index.tsx b/projects/app/src/pages/login/index.tsx index 83d853744..10e649370 100644 --- a/projects/app/src/pages/login/index.tsx +++ b/projects/app/src/pages/login/index.tsx @@ -9,10 +9,11 @@ import { useChatStore } from '@/web/core/chat/storeChat'; import LoginForm from './components/LoginForm'; import dynamic from 'next/dynamic'; import { serviceSideProps } from '@/web/common/utils/i18n'; -import { setToken } from '@/web/support/user/auth'; +import { clearToken, setToken } from '@/web/support/user/auth'; import { feConfigs } from '@/web/common/system/staticData'; import CommunityModal from '@/components/CommunityModal'; import Script from 'next/script'; +import { loginOut } from '@/web/support/user/api'; const RegisterForm = dynamic(() => import('./components/RegisterForm')); const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm')); @@ -53,6 +54,7 @@ const Login = () => { } useEffect(() => { + clearToken(); router.prefetch('/app/list'); }, []); diff --git a/projects/app/src/pages/login/provider.tsx b/projects/app/src/pages/login/provider.tsx index a68935e97..ee8c4096e 100644 --- a/projects/app/src/pages/login/provider.tsx +++ b/projects/app/src/pages/login/provider.tsx @@ -4,7 +4,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import type { ResLogin } from '@/global/support/api/userRes.d'; import { useChatStore } from '@/web/core/chat/storeChat'; import { useUserStore } from '@/web/support/user/useUserStore'; -import { setToken } from '@/web/support/user/auth'; +import { clearToken, setToken } from '@/web/support/user/auth'; import { oauthLogin } from '@/web/support/user/api'; import { useToast } from '@/web/common/hooks/useToast'; import Loading from '@/components/Loading'; @@ -21,12 +21,13 @@ const provider = ({ code, state }: { code: string; state: string }) => { const loginSuccess = useCallback( (res: ResLogin) => { + setToken(res.token); + setUserInfo(res.user); + // init store setLastChatId(''); setLastChatAppId(''); - setUserInfo(res.user); - setToken(res.token); setTimeout(() => { router.push( loginStore?.lastRoute ? decodeURIComponent(loginStore?.lastRoute) : '/app/list' @@ -72,8 +73,11 @@ const provider = ({ code, state }: { code: string; state: string }) => { [loginStore, loginSuccess, router, toast] ); - useQuery(['init', code], () => { + useEffect(() => { + clearToken(); + router.prefetch('/app/list'); if (!code) return; + if (state !== loginStore?.state) { toast({ status: 'warning', @@ -85,11 +89,6 @@ const provider = ({ code, state }: { code: string; state: string }) => { return; } authCode(code); - return null; - }); - - useEffect(() => { - router.prefetch('/app/list'); }, []); return ; diff --git a/projects/app/src/pages/plugin/edit/Header.tsx b/projects/app/src/pages/plugin/edit/Header.tsx index 81686fac9..d4b4301e4 100644 --- a/projects/app/src/pages/plugin/edit/Header.tsx +++ b/projects/app/src/pages/plugin/edit/Header.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react'; import { PluginItemSchema } from '@fastgpt/global/core/plugin/type'; import { useRequest } from '@/web/common/hooks/useRequest'; @@ -42,12 +42,21 @@ const Header = ({ plugin, onClose }: Props) => { return Promise.reject(t('module.Plugin input must connect')); } } - - if (item.inputs.find((input) => input.required && !input.connected)) { - return Promise.reject(`【${item.name}】存在未连接的必填输入`); + if ( + item.flowType === FlowNodeTypeEnum.pluginOutput && + item.inputs.find((input) => !input.connected) + ) { + return Promise.reject(t('core.module.Plugin output must connect')); } - if (item.inputs.find((input) => input.valueCheck && !input.valueCheck(input.value))) { - return Promise.reject(`【${item.name}】存在为填写的必填项`); + + if ( + item.inputs.find((input) => { + if (!input.required || input.connected) return false; + if (!input.value || input.value === '' || input.value?.length === 0) return true; + return false; + }) + ) { + return Promise.reject(`【${item.name}】存在未填或未连接参数`); } } diff --git a/projects/app/src/pages/plugin/edit/Preview.tsx b/projects/app/src/pages/plugin/edit/Preview.tsx index 8b62c3ac8..ecb5d2599 100644 --- a/projects/app/src/pages/plugin/edit/Preview.tsx +++ b/projects/app/src/pages/plugin/edit/Preview.tsx @@ -3,7 +3,7 @@ import ReactFlow, { Background, ReactFlowProvider, useNodesState } from 'reactfl import { FlowModuleItemType, ModuleItemType } from '@fastgpt/global/core/module/type'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; import dynamic from 'next/dynamic'; -import { formatPluginIOModules } from '@fastgpt/global/core/module/utils'; +import { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils'; import MyModal from '@/components/MyModal'; import { Box } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; @@ -12,7 +12,7 @@ import { appModule2FlowNode } from '@/utils/adapt'; const nodeTypes = { [FlowNodeTypeEnum.pluginModule]: dynamic( - () => import('@/components/core/module/Flow/components/nodes/NodePreviewPlugin') + () => import('@/components/core/module/Flow/components/nodes/NodeSimple') ) }; @@ -36,16 +36,21 @@ const PreviewPlugin = ({ flowType: FlowNodeTypeEnum.pluginModule, logo: plugin.avatar, name: plugin.name, - description: plugin.intro, intro: plugin.intro, - ...formatPluginIOModules(plugin._id, modules) + ...formatPluginToPreviewModule(plugin._id, modules) } }) ]); }, [modules, plugin, setNodes]); return ( - + { const router = useRouter(); const { toast } = useToast(); const { nodes = [] } = useFlowProviderStore(); - const { pluginModuleTemplates, loadPluginModuleTemplates } = usePluginStore(); + const { pluginModuleTemplates, loadPluginTemplates } = usePluginStore(); const filterTemplates = useMemo(() => { - const copyTemplates: SystemModuleTemplateType = JSON.parse( - JSON.stringify(PluginModuleTemplates) + const copyTemplates: FlowModuleTemplateType[] = JSON.parse( + JSON.stringify(pluginSystemModuleTemplates) ); const filterType: Record = { [FlowNodeTypeEnum.userGuide]: 1, @@ -37,12 +37,10 @@ const Render = ({ pluginId }: Props) => { // filter some template nodes.forEach((node) => { if (node.type && filterType[node.type]) { - copyTemplates.forEach((item) => { - item.list.forEach((module, index) => { - if (module.flowType === node.type) { - item.list.splice(index, 1); - } - }); + copyTemplates.forEach((module, index) => { + if (module.flowType === node.type) { + copyTemplates.splice(index, 1); + } }); } }); @@ -60,16 +58,15 @@ const Render = ({ pluginId }: Props) => { } }); - useQuery(['getUserPlugs2ModuleTemplates'], () => loadPluginModuleTemplates()); - const filterPlugins = useMemo( - () => pluginModuleTemplates.filter((item) => item.id !== pluginId), - [pluginId, pluginModuleTemplates] - ); + useQuery(['getPlugTemplates'], () => loadPluginTemplates()); + const filterPlugins = useMemo(() => { + return pluginModuleTemplates.filter((item) => item.id !== pluginId); + }, [pluginId, pluginModuleTemplates]); return data ? ( router.back()} />} /> @@ -80,7 +77,7 @@ const Render = ({ pluginId }: Props) => { export default function AdEdit(props: any) { return ( - + ); diff --git a/projects/app/src/pages/plugin/list/component/EditModal.tsx b/projects/app/src/pages/plugin/list/component/EditModal.tsx index 74865a10e..71f0696d2 100644 --- a/projects/app/src/pages/plugin/list/component/EditModal.tsx +++ b/projects/app/src/pages/plugin/list/component/EditModal.tsx @@ -134,10 +134,13 @@ const CreateModal = ({ }, [defaultValue.id, onClose, toast, t, onDelete]); return ( - - - {defaultValue.id ? t('plugin.Update Your Plugin') : t('plugin.Create Your Plugin')} - + {t('plugin.Set Name')} diff --git a/projects/app/src/service/core/app/utils.ts b/projects/app/src/service/core/app/utils.ts new file mode 100644 index 000000000..ca9416ba0 --- /dev/null +++ b/projects/app/src/service/core/app/utils.ts @@ -0,0 +1,12 @@ +import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type'; +import { GET } from '@fastgpt/service/common/api/plusRequest'; + +export async function getSimpleTemplatesFromPlus(): Promise { + try { + if (!global.systemEnv.pluginBaseUrl) return []; + + return GET('/core/app/getSimpleTemplates'); + } catch (error) { + return []; + } +} diff --git a/projects/app/src/service/core/dataset/data/controller.ts b/projects/app/src/service/core/dataset/data/controller.ts index 4a7281ba4..f04a1e8a5 100644 --- a/projects/app/src/service/core/dataset/data/controller.ts +++ b/projects/app/src/service/core/dataset/data/controller.ts @@ -27,6 +27,7 @@ export async function insertData2Dataset({ model: string; }) { if (!q || !datasetId || !collectionId || !model) { + console.log(q, a, datasetId, collectionId, model); return Promise.reject('q, datasetId, collectionId, model is required'); } if (String(teamId) === String(tmbId)) { diff --git a/projects/app/src/service/core/dataset/data/pg.ts b/projects/app/src/service/core/dataset/data/pg.ts index 570555f13..94c8f3563 100644 --- a/projects/app/src/service/core/dataset/data/pg.ts +++ b/projects/app/src/service/core/dataset/data/pg.ts @@ -153,11 +153,11 @@ export async function searchDatasetData({ SET LOCAL hnsw.ef_search = ${global.systemEnv.pgHNSWEfSearch || 100}; select id, collection_id, data_id, (vector <#> '[${ vectors[0] - }]') * -1 AS score from ${PgDatasetTableName} where dataset_id IN (${datasetIds - .map((id) => `'${String(id)}'`) - .join(',')}) AND vector <#> '[${vectors[0]}]' < -${similarity} order by vector <#> '[${ + }]') * -1 AS score from ${PgDatasetTableName} + where dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')}) AND vector <#> '[${ vectors[0] - }]' limit ${minLimit}; + }]' < -${similarity} + order by score desc limit ${minLimit}; COMMIT;` ); diff --git a/projects/app/src/service/core/modules/constant.ts b/projects/app/src/service/core/modules/constant.ts new file mode 100644 index 000000000..ebdf9c313 --- /dev/null +++ b/projects/app/src/service/core/modules/constant.ts @@ -0,0 +1,7 @@ +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; + +export const initRunningModuleType: Record = { + [FlowNodeTypeEnum.historyNode]: true, + [FlowNodeTypeEnum.questionInput]: true, + [FlowNodeTypeEnum.pluginInput]: true +}; diff --git a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts index 6428fe71e..e71673b92 100644 --- a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts +++ b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts @@ -1,11 +1,10 @@ import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt'; import { ChatContextFilter } from '@fastgpt/service/core/chat/utils'; import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d'; -import { ChatRoleEnum, TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; +import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { getAIApi } from '@fastgpt/service/core/ai/config'; import type { ClassifyQuestionAgentItemType } from '@fastgpt/global/core/module/type.d'; -import { SystemInputEnum } from '@/constants/app'; -import { FlowNodeSpecialInputKeyEnum } from '@fastgpt/global/core/module/node/constant'; +import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { Prompt_CQJson } from '@/global/core/prompt/agent'; @@ -13,14 +12,14 @@ import { FunctionModelItemType } from '@fastgpt/global/core/ai/model.d'; import { getCQModel } from '@/service/core/ai/model'; type Props = ModuleDispatchProps<{ - model: string; - systemPrompt?: string; - history?: ChatItemType[]; - [SystemInputEnum.userChatInput]: string; - [FlowNodeSpecialInputKeyEnum.agents]: ClassifyQuestionAgentItemType[]; + [ModuleInputKeyEnum.aiModel]: string; + [ModuleInputKeyEnum.aiSystemPrompt]?: string; + [ModuleInputKeyEnum.history]?: ChatItemType[]; + [ModuleInputKeyEnum.userChatInput]: string; + [ModuleInputKeyEnum.agents]: ClassifyQuestionAgentItemType[]; }>; type CQResponse = { - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; [key: string]: any; }; @@ -56,9 +55,10 @@ export const dispatchClassifyQuestion = async (props: Props): Promise; type Response = { - [ContextExtractEnum.success]?: boolean; - [ContextExtractEnum.failed]?: boolean; - [ContextExtractEnum.fields]: string; - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.success]?: boolean; + [ModuleOutputKeyEnum.failed]?: boolean; + [ModuleOutputKeyEnum.contextExtractFields]: string; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; }; const agentFunName = 'agent_extract_data'; @@ -70,13 +70,14 @@ export async function dispatchContentExtract(props: Props): Promise { } return { - [ContextExtractEnum.success]: success ? true : undefined, - [ContextExtractEnum.failed]: success ? undefined : true, - [ContextExtractEnum.fields]: JSON.stringify(arg), + [ModuleOutputKeyEnum.success]: success ? true : undefined, + [ModuleOutputKeyEnum.failed]: success ? undefined : true, + [ModuleOutputKeyEnum.contextExtractFields]: JSON.stringify(arg), ...arg, - [TaskResponseKeyEnum.responseData]: { + [ModuleOutputKeyEnum.responseData]: { price: user.openaiAccount?.key ? 0 : extractModel.price * tokens, model: extractModel.name || '', + query: content, tokens, extractDescription: description, extractResult: arg diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts index 2dfc910fd..e0fb0eef3 100644 --- a/projects/app/src/service/moduleDispatch/chat/oneapi.ts +++ b/projects/app/src/service/moduleDispatch/chat/oneapi.ts @@ -6,7 +6,6 @@ import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant' import { textAdaptGptResponse } from '@/utils/adapt'; import { getAIApi } from '@fastgpt/service/core/ai/config'; import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import { countModelPrice } from '@/service/support/wallet/bill/utils'; import type { ChatModelItemType } from '@fastgpt/global/core/ai/model.d'; import { postTextCensor } from '@/service/common/censor'; @@ -15,26 +14,26 @@ import type { ModuleItemType } from '@fastgpt/global/core/module/type.d'; import { countMessagesTokens, sliceMessagesTB } from '@fastgpt/global/common/string/tiktoken'; import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt'; import { Prompt_QuotePromptList, Prompt_QuoteTemplateList } from '@/global/core/prompt/AIChat'; -import type { AIChatProps } from '@/types/core/aiChat'; +import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d'; import { replaceVariable } from '@fastgpt/global/common/string/tools'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { responseWrite, responseWriteController } from '@fastgpt/service/common/response'; import { getChatModel, ModelTypeEnum } from '@/service/core/ai/model'; import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { formatStr2ChatContent } from '@fastgpt/service/core/chat/utils'; +import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; export type ChatProps = ModuleDispatchProps< - AIChatProps & { - userChatInput: string; - history?: ChatItemType[]; - quoteQA?: SearchDataResponseItemType[]; - limitPrompt?: string; + AIChatModuleProps & { + [ModuleInputKeyEnum.userChatInput]: string; + [ModuleInputKeyEnum.history]?: ChatItemType[]; + [ModuleInputKeyEnum.aiChatDatasetQuote]?: SearchDataResponseItemType[]; } >; export type ChatResponse = { - [TaskResponseKeyEnum.answerText]: string; - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; - [TaskResponseKeyEnum.history]: ChatItemType[]; + [ModuleOutputKeyEnum.answerText]: string; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.history]: ChatItemType[]; }; /* request openai chat */ @@ -54,7 +53,6 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise tokensLimit ? tokensLimit - promptsToken : maxToken; + maxToken = promptsToken + maxToken > tokensLimit ? tokensLimit - promptsToken : maxToken; return { max_tokens: maxToken @@ -330,7 +317,7 @@ function targetResponse({ detail: boolean; }) { const targets = - outputs.find((output) => output.key === TaskResponseKeyEnum.answerText)?.targets || []; + outputs.find((output) => output.key === ModuleOutputKeyEnum.answerText)?.targets || []; if (targets.length === 0) return; responseWrite({ diff --git a/projects/app/src/service/moduleDispatch/dataset/search.ts b/projects/app/src/service/moduleDispatch/dataset/search.ts index 6ab8ade9d..2292870f3 100644 --- a/projects/app/src/service/moduleDispatch/dataset/search.ts +++ b/projects/app/src/service/moduleDispatch/dataset/search.ts @@ -1,28 +1,32 @@ import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import { countModelPrice } from '@/service/support/wallet/bill/utils'; import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d'; import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { ModelTypeEnum } from '@/service/core/ai/model'; import { searchDatasetData } from '@/service/core/dataset/data/pg'; +import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; type DatasetSearchProps = ModuleDispatchProps<{ - datasets: SelectedDatasetType; - similarity: number; - limit: number; - rerank: boolean; - userChatInput: string; + [ModuleInputKeyEnum.datasetSelectList]: SelectedDatasetType; + [ModuleInputKeyEnum.datasetSimilarity]: number; + [ModuleInputKeyEnum.datasetLimit]: number; + [ModuleInputKeyEnum.datasetStartReRank]: boolean; + [ModuleInputKeyEnum.userChatInput]: string; }>; -export type KBSearchResponse = { - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; - isEmpty?: boolean; - unEmpty?: boolean; - quoteQA: SearchDataResponseItemType[]; +export type DatasetSearchResponse = { + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.datasetIsEmpty]?: boolean; + [ModuleOutputKeyEnum.datasetUnEmpty]?: boolean; + [ModuleOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[]; }; -export async function dispatchDatasetSearch(props: DatasetSearchProps): Promise { +export async function dispatchDatasetSearch( + props: DatasetSearchProps +): Promise { const { + teamId, + tmbId, inputs: { datasets = [], similarity = 0.4, limit = 5, rerank, userChatInput } } = props as DatasetSearchProps; @@ -56,6 +60,7 @@ export async function dispatchDatasetSearch(props: DatasetSearchProps): Promise< tokens: tokenLen, type: ModelTypeEnum.vector }), + query: userChatInput, model: vectorModel.name, tokens: tokenLen, similarity, diff --git a/projects/app/src/service/moduleDispatch/index.ts b/projects/app/src/service/moduleDispatch/index.ts index 7a595d227..64111cd30 100644 --- a/projects/app/src/service/moduleDispatch/index.ts +++ b/projects/app/src/service/moduleDispatch/index.ts @@ -1,17 +1,17 @@ import { NextApiResponse } from 'next'; -import { SystemInputEnum, SystemOutputEnum } from '@/constants/app'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; import { RunningModuleItemType } from '@/types/app'; import { ModuleDispatchProps } from '@/types/core/chat/type'; import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; import { ModuleItemType } from '@fastgpt/global/core/module/type'; import { UserType } from '@fastgpt/global/support/user/type'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { responseWrite } from '@fastgpt/service/common/response'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { getSystemTime } from '@fastgpt/global/common/time/timezone'; -import { initModuleType } from '@/constants/flow'; +import { initRunningModuleType } from '../core/modules/constant'; import { dispatchHistory } from './init/history'; import { dispatchChatInput } from './init/userChatInput'; @@ -29,6 +29,7 @@ import { dispatchPluginOutput } from './plugin/runOutput'; /* running */ export async function dispatchModules({ res, + appId, chatId, modules, user, @@ -40,6 +41,7 @@ export async function dispatchModules({ detail = false }: { res: NextApiResponse; + appId: string; chatId?: string; modules: ModuleItemType[]; user: UserType; @@ -93,60 +95,66 @@ export async function dispatchModules({ runningTime = time; const isResponseAnswerText = - inputs.find((item) => item.key === SystemInputEnum.isResponseAnswerText)?.value ?? true; + inputs.find((item) => item.key === ModuleInputKeyEnum.aiChatIsResponseText)?.value ?? true; if (isResponseAnswerText) { chatAnswerText += answerText; } } - function moduleInput( - module: RunningModuleItemType, - data: Record = {} - ): Promise { - const checkInputFinish = () => { - return !module.inputs.find((item: any) => item.value === undefined); - }; + function moduleInput(module: RunningModuleItemType, data: Record = {}) { const updateInputValue = (key: string, value: any) => { const index = module.inputs.findIndex((item: any) => item.key === key); if (index === -1) return; module.inputs[index].value = value; }; - - const set = new Set(); - - return Promise.all( - Object.entries(data).map(([key, val]: any) => { - updateInputValue(key, val); - - if (!set.has(module.moduleId) && checkInputFinish()) { - set.add(module.moduleId); - // remove switch - updateInputValue(SystemInputEnum.switch, undefined); - return moduleRun(module); - } - }) - ); + Object.entries(data).map(([key, val]: any) => { + updateInputValue(key, val); + }); + return; } function moduleOutput( module: RunningModuleItemType, result: Record = {} ): Promise { pushStore(module, result); + + const nextRunModules: RunningModuleItemType[] = []; + + // Assign the output value to the next module + module.outputs.map((outputItem) => { + if (result[outputItem.key] === undefined) return; + /* update output value */ + outputItem.value = result[outputItem.key]; + + /* update target */ + outputItem.targets.map((target: any) => { + // find module + const targetModule = runningModules.find((item) => item.moduleId === target.moduleId); + if (!targetModule) return; + + // push to running queue + nextRunModules.push(targetModule); + + // update input + moduleInput(targetModule, { [target.key]: outputItem.value }); + }); + }); + + return checkModulesCanRun(nextRunModules); + } + function checkModulesCanRun(modules: RunningModuleItemType[] = []) { + const set = new Set(); + const filterModules = modules.filter((module) => { + if (set.has(module.moduleId)) return false; + set.add(module.moduleId); + return true; + }); + return Promise.all( - module.outputs.map((outputItem) => { - if (result[outputItem.key] === undefined) return; - /* update output value */ - outputItem.value = result[outputItem.key]; - - /* update target */ - return Promise.all( - outputItem.targets.map((target: any) => { - // find module - const targetModule = runningModules.find((item) => item.moduleId === target.moduleId); - if (!targetModule) return; - - return moduleInput(targetModule, { [target.key]: outputItem.value }); - }) - ); + filterModules.map((module) => { + if (!module.inputs.find((item: any) => item.value === undefined)) { + moduleInput(module, { [ModuleInputKeyEnum.switch]: undefined }); + return moduleRun(module); + } }) ); } @@ -168,6 +176,8 @@ export async function dispatchModules({ }); const props: ModuleDispatchProps> = { res, + appId, + chatId, stream, detail, variables, @@ -175,7 +185,6 @@ export async function dispatchModules({ user, teamId, tmbId, - chatId, inputs: params }; @@ -201,29 +210,29 @@ export async function dispatchModules({ })(); const formatResponseData = (() => { - if (!dispatchRes[TaskResponseKeyEnum.responseData]) return undefined; - if (Array.isArray(dispatchRes[TaskResponseKeyEnum.responseData])) - return dispatchRes[TaskResponseKeyEnum.responseData]; + if (!dispatchRes[ModuleOutputKeyEnum.responseData]) return undefined; + if (Array.isArray(dispatchRes[ModuleOutputKeyEnum.responseData])) + return dispatchRes[ModuleOutputKeyEnum.responseData]; return { - ...dispatchRes[TaskResponseKeyEnum.responseData], moduleName: module.name, - moduleType: module.flowType + moduleType: module.flowType, + ...dispatchRes[ModuleOutputKeyEnum.responseData] }; })(); return moduleOutput(module, { - [SystemOutputEnum.finish]: true, + [ModuleOutputKeyEnum.finish]: true, ...dispatchRes, - [TaskResponseKeyEnum.responseData]: formatResponseData + [ModuleOutputKeyEnum.responseData]: formatResponseData }); } // start process width initInput - const initModules = runningModules.filter((item) => initModuleType[item.flowType]); + const initModules = runningModules.filter((item) => initRunningModuleType[item.flowType]); + initModules.map((module) => moduleInput(module, params)); + await checkModulesCanRun(initModules); - await Promise.all(initModules.map((module) => moduleInput(module, params))); - - // focus running pluginOutput + // focus try to run pluginOutput const pluginOutputModule = runningModules.find( (item) => item.flowType === FlowNodeTypeEnum.pluginOutput ); @@ -232,8 +241,8 @@ export async function dispatchModules({ } return { - [TaskResponseKeyEnum.answerText]: chatAnswerText, - [TaskResponseKeyEnum.responseData]: chatResponse + [ModuleOutputKeyEnum.answerText]: chatAnswerText, + [ModuleOutputKeyEnum.responseData]: chatResponse }; } @@ -249,7 +258,7 @@ function loadModules( flowType: module.flowType, showStatus: module.showStatus, inputs: module.inputs - .filter((item) => item.connected) // filter unconnected target input + .filter((item) => item.connected || item.value !== undefined) // filter unconnected target input .map((item) => { if (typeof item.value !== 'string') { return { @@ -269,14 +278,14 @@ function loadModules( outputs: module.outputs .map((item) => ({ key: item.key, - answer: item.key === TaskResponseKeyEnum.answerText, + answer: item.key === ModuleOutputKeyEnum.answerText, value: undefined, targets: item.targets })) .sort((a, b) => { // finish output always at last - if (a.key === SystemOutputEnum.finish) return 1; - if (b.key === SystemOutputEnum.finish) return -1; + if (a.key === ModuleOutputKeyEnum.finish) return 1; + if (b.key === ModuleOutputKeyEnum.finish) return -1; return 0; }) }; diff --git a/projects/app/src/service/moduleDispatch/init/history.tsx b/projects/app/src/service/moduleDispatch/init/history.tsx index b1cdf312d..01abbced6 100644 --- a/projects/app/src/service/moduleDispatch/init/history.tsx +++ b/projects/app/src/service/moduleDispatch/init/history.tsx @@ -1,9 +1,9 @@ -import { SystemInputEnum } from '@/constants/app'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; export type HistoryProps = ModuleDispatchProps<{ maxContext: number; - [SystemInputEnum.history]: ChatItemType[]; + [ModuleInputKeyEnum.history]: ChatItemType[]; }>; export const dispatchHistory = (props: Record) => { diff --git a/projects/app/src/service/moduleDispatch/init/userChatInput.tsx b/projects/app/src/service/moduleDispatch/init/userChatInput.tsx index b36101097..605060faa 100644 --- a/projects/app/src/service/moduleDispatch/init/userChatInput.tsx +++ b/projects/app/src/service/moduleDispatch/init/userChatInput.tsx @@ -1,7 +1,7 @@ -import { SystemInputEnum } from '@/constants/app'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; export type UserChatInputProps = ModuleDispatchProps<{ - [SystemInputEnum.userChatInput]: string; + [ModuleInputKeyEnum.userChatInput]: string; }>; export const dispatchChatInput = (props: Record) => { diff --git a/projects/app/src/service/moduleDispatch/plugin/run.ts b/projects/app/src/service/moduleDispatch/plugin/run.ts index 020cd2f92..361823baf 100644 --- a/projects/app/src/service/moduleDispatch/plugin/run.ts +++ b/projects/app/src/service/moduleDispatch/plugin/run.ts @@ -1,24 +1,24 @@ import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { dispatchModules } from '../index'; -import { - FlowNodeSpecialInputKeyEnum, - FlowNodeTypeEnum -} from '@fastgpt/global/core/module/node/constant'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; -import { MongoPlugin } from '@fastgpt/service/core/plugin/schema'; +import { getPluginRuntimeById } from '@fastgpt/service/core/plugin/controller'; +import { authPluginCanUse } from '@fastgpt/service/support/permission/auth/plugin'; type RunPluginProps = ModuleDispatchProps<{ - [FlowNodeSpecialInputKeyEnum.pluginId]: string; + [ModuleInputKeyEnum.pluginId]: string; [key: string]: any; }>; type RunPluginResponse = { - answerText: string; - [TaskResponseKeyEnum.responseData]?: moduleDispatchResType[]; + [ModuleOutputKeyEnum.answerText]: string; + [ModuleOutputKeyEnum.responseData]?: moduleDispatchResType; }; export const dispatchRunPlugin = async (props: RunPluginProps): Promise => { const { + teamId, + tmbId, inputs: { pluginId, ...data } } = props; @@ -26,14 +26,15 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise ({ + ...module, + showStatus: false + })), params: data }); @@ -45,10 +46,12 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise item.moduleType !== FlowNodeTypeEnum.pluginOutput - ), + responseData: { + moduleLogo: plugin.avatar, + price: responseData.reduce((sum, item) => sum + item.price, 0), + runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0), + pluginOutput: output?.pluginOutput + }, ...(output ? output.pluginOutput : {}) }; }; diff --git a/projects/app/src/service/moduleDispatch/plugin/runOutput.ts b/projects/app/src/service/moduleDispatch/plugin/runOutput.ts index 47cb992c3..b300f2c89 100644 --- a/projects/app/src/service/moduleDispatch/plugin/runOutput.ts +++ b/projects/app/src/service/moduleDispatch/plugin/runOutput.ts @@ -1,19 +1,19 @@ import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; export type PluginOutputProps = ModuleDispatchProps<{ [key: string]: any; }>; export type PluginOutputResponse = { - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; }; export const dispatchPluginOutput = (props: PluginOutputProps): PluginOutputResponse => { const { inputs } = props; return { - [TaskResponseKeyEnum.responseData]: { + responseData: { price: 0, pluginOutput: inputs } diff --git a/projects/app/src/service/moduleDispatch/tools/answer.ts b/projects/app/src/service/moduleDispatch/tools/answer.ts index a77d7b72d..86345c740 100644 --- a/projects/app/src/service/moduleDispatch/tools/answer.ts +++ b/projects/app/src/service/moduleDispatch/tools/answer.ts @@ -1,13 +1,13 @@ -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { responseWrite } from '@fastgpt/service/common/response'; import { textAdaptGptResponse } from '@/utils/adapt'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; export type AnswerProps = ModuleDispatchProps<{ text: string; }>; export type AnswerResponse = { - [TaskResponseKeyEnum.answerText]: string; + [ModuleOutputKeyEnum.answerText]: string; }; export const dispatchAnswer = (props: Record): AnswerResponse => { @@ -31,6 +31,6 @@ export const dispatchAnswer = (props: Record): AnswerResponse => { } return { - [TaskResponseKeyEnum.answerText]: formatText + answerText: formatText }; }; diff --git a/projects/app/src/service/moduleDispatch/tools/http.ts b/projects/app/src/service/moduleDispatch/tools/http.ts index 1c0f58375..93e79eb6a 100644 --- a/projects/app/src/service/moduleDispatch/tools/http.ts +++ b/projects/app/src/service/moduleDispatch/tools/http.ts @@ -1,14 +1,14 @@ -import { HttpPropsEnum } from '@/constants/flow/flowField'; import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; +import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; + export type HttpRequestProps = ModuleDispatchProps<{ - [HttpPropsEnum.url]: string; + [ModuleInputKeyEnum.httpUrl]: string; [key: string]: any; }>; export type HttpResponse = { - [HttpPropsEnum.failed]?: boolean; - [TaskResponseKeyEnum.responseData]: moduleDispatchResType; + [ModuleOutputKeyEnum.failed]?: boolean; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType; [key: string]: any; }; @@ -32,7 +32,7 @@ export const dispatchHttpRequest = async (props: Record): Promise): Promise; type Response = { - [TaskResponseKeyEnum.responseData]: moduleDispatchResType[]; - [TaskResponseKeyEnum.answerText]: string; - [TaskResponseKeyEnum.history]: ChatItemType[]; + [ModuleOutputKeyEnum.responseData]: moduleDispatchResType[]; + [ModuleOutputKeyEnum.answerText]: string; + [ModuleOutputKeyEnum.history]: ChatItemType[]; }; export const dispatchAppRequest = async (props: Props): Promise => { @@ -53,6 +54,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { const { responseData, answerText } = await dispatchModules({ ...props, + appId: app.id, modules: appData.modules, params: { history, @@ -73,7 +75,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { return { responseData, - [TaskResponseKeyEnum.answerText]: answerText, - [TaskResponseKeyEnum.history]: completeMessages + answerText: answerText, + history: completeMessages }; }; diff --git a/projects/app/src/service/mongo.ts b/projects/app/src/service/mongo.ts index f46727e5c..c4d919efb 100644 --- a/projects/app/src/service/mongo.ts +++ b/projects/app/src/service/mongo.ts @@ -4,7 +4,7 @@ import { initPg } from '@fastgpt/service/common/pg'; import { MongoUser } from '@fastgpt/service/support/user/schema'; import { connectMongo } from '@fastgpt/service/common/mongo/init'; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { getInitConfig, initGlobal } from '@/pages/api/system/getInitData'; +import { getInitConfig } from '@/pages/api/system/getInitData'; import { createDefaultTeam } from '@fastgpt/service/support/user/team/controller'; import { exit } from 'process'; @@ -14,7 +14,6 @@ import { exit } from 'process'; export function connectToDatabase(): Promise { return connectMongo({ beforeHook: () => { - initGlobal(); getInitConfig(); }, afterHook: () => { diff --git a/projects/app/src/types/app.d.ts b/projects/app/src/types/app.d.ts index 79db1358f..d7b6c6944 100644 --- a/projects/app/src/types/app.d.ts +++ b/projects/app/src/types/app.d.ts @@ -1,10 +1,7 @@ -import { FlowNodeTypeEnum, FlowNodeValTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants'; import { XYPosition } from 'reactflow'; -import { - AppModuleItemTypeEnum, - ModulesInputItemTypeEnum, - VariableInputEnum -} from '../constants/app'; +import { AppModuleItemTypeEnum, ModulesInputItemTypeEnum } from '../constants/app'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import type { FlowNodeInputItemType, @@ -26,23 +23,6 @@ export interface ShareAppItem { isCollection: boolean; } -export type VariableItemType = { - id: string; - key: string; - label: string; - type: `${VariableInputEnum}`; - required: boolean; - maxLen: number; - enums: { value: string }[]; -}; - -export type AppTTSConfigType = { - type: 'none' | 'web' | 'model'; - model?: string; - voice?: string; - speed?: number; -}; - /* app module */ export type AppItemType = { id: string; diff --git a/projects/app/src/types/core/aiChat.d.ts b/projects/app/src/types/core/aiChat.d.ts deleted file mode 100644 index 1034bd2e4..000000000 --- a/projects/app/src/types/core/aiChat.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { SystemInputEnum } from '@/constants/app'; - -/* ai chat modules props */ -export type AIChatProps = { - model: string; - systemPrompt?: string; - temperature: number; - maxToken: number; - [SystemInputEnum.isResponseAnswerText]: boolean; - quoteTemplate?: string; - quotePrompt?: string; - frequency: number; - presence: number; -}; diff --git a/projects/app/src/types/core/chat/type.d.ts b/projects/app/src/types/core/chat/type.d.ts index 45eae4bfa..7d19bfb36 100644 --- a/projects/app/src/types/core/chat/type.d.ts +++ b/projects/app/src/types/core/chat/type.d.ts @@ -5,6 +5,7 @@ import type { UserType } from '@fastgpt/global/support/user/type'; // module dispatch props type export type ModuleDispatchProps = { res: NextApiResponse; + appId: string; chatId?: string; stream: boolean; detail: boolean; diff --git a/projects/app/src/types/index.d.ts b/projects/app/src/types/index.d.ts index 173262084..25120643f 100644 --- a/projects/app/src/types/index.d.ts +++ b/projects/app/src/types/index.d.ts @@ -7,6 +7,7 @@ import { WhisperModelType } from '@fastgpt/global/core/ai/model.d'; import { TrackEventName } from '@/constants/common'; +import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type'; export type PagingData = { pageNum: number; @@ -33,6 +34,8 @@ declare global { var priceMd: string; var systemVersion: string; + var simpleModeTemplates: AppSimpleEditConfigTemplateType[]; + interface Window { ['pdfjs-dist/build/pdf']: any; grecaptcha: any; diff --git a/projects/app/src/utils/adapt.ts b/projects/app/src/utils/adapt.ts index 26ba8f55c..1fff51d89 100644 --- a/projects/app/src/utils/adapt.ts +++ b/projects/app/src/utils/adapt.ts @@ -2,9 +2,10 @@ import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d'; import type { ModuleItemType, FlowModuleItemType } from '@fastgpt/global/core/module/type.d'; import type { Edge, Node } from 'reactflow'; -import { connectionLineStyle } from '@/constants/flow'; +import { connectionLineStyle } from '@/web/core/modules/constants/flowUi'; import { customAlphabet } from 'nanoid'; -import { EmptyModule, ModuleTemplatesFlat } from '@/constants/flow/ModuleTemplate'; +import { EmptyModule } from '@fastgpt/global/core/module/template/system/empty'; +import { moduleTemplatesFlat } from '@/web/core/modules/template/system'; import { adaptRole_Message2Chat } from '@fastgpt/global/core/chat/adapt'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); @@ -44,7 +45,7 @@ export const appModule2FlowNode = ({ }): Node => { // init some static data const template = - ModuleTemplatesFlat.find((template) => template.flowType === item.flowType) || EmptyModule; + moduleTemplatesFlat.find((template) => template.flowType === item.flowType) || EmptyModule; const concatInputs = template.inputs.concat( item.inputs.filter( diff --git a/projects/app/src/web/common/api/fetch.ts b/projects/app/src/web/common/api/fetch.ts index 3f32bbebb..ce7a7adfc 100644 --- a/projects/app/src/web/common/api/fetch.ts +++ b/projects/app/src/web/common/api/fetch.ts @@ -1,10 +1,10 @@ -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { getErrText } from '@fastgpt/global/common/error/utils'; import { parseStreamChunk, SSEParseData } from '@/utils/sse'; import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d'; import { StartChatFnProps } from '@/components/ChatBox'; import { getToken } from '@/web/support/user/auth'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; type StreamFetchProps = { url?: string; @@ -12,16 +12,17 @@ type StreamFetchProps = { onMessage: StartChatFnProps['generatingMessage']; abortSignal: AbortController; }; +type StreamResponseType = { + responseText: string; + [ModuleOutputKeyEnum.responseData]: ChatHistoryItemResType[]; +}; export const streamFetch = ({ url = '/api/v1/chat/completions', data, onMessage, abortSignal }: StreamFetchProps) => - new Promise<{ - responseText: string; - [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType[]; - }>(async (resolve, reject) => { + new Promise(async (resolve, reject) => { try { const response = await window.fetch(url, { method: 'POST', diff --git a/projects/app/src/web/common/file/utils.ts b/projects/app/src/web/common/file/utils.ts index 903b6767a..f6fc5450e 100644 --- a/projects/app/src/web/common/file/utils.ts +++ b/projects/app/src/web/common/file/utils.ts @@ -28,6 +28,16 @@ export const readTxtContent = (file: File) => { */ export const readPdfContent = (file: File) => new Promise((resolve, reject) => { + type TokenType = { + str: string; + dir: string; + width: number; + height: number; + transform: number[]; + fontName: string; + hasEOL: boolean; + }; + try { const pdfjsLib = window['pdfjs-dist/build/pdf']; pdfjsLib.workerSrc = '/js/pdf.worker.js'; @@ -36,9 +46,19 @@ export const readPdfContent = (file: File) => const page = await doc.getPage(pageNo); const tokenizedText = await page.getTextContent(); + const viewport = page.getViewport({ scale: 1 }); + const pageHeight = viewport.height; + const headerThreshold = pageHeight * 0.07; // 假设页头在页面顶部5%的区域内 + const footerThreshold = pageHeight * 0.93; // 假设页脚在页面底部5%的区域内 + const pageText = tokenizedText.items - .map((token: any) => token.str) - .filter((item: string) => item) + .filter((token: TokenType) => { + return ( + !token.transform || + (token.transform[5] > headerThreshold && token.transform[5] < footerThreshold) + ); + }) + .map((token: TokenType) => token.str) .join(''); return pageText; }; @@ -54,7 +74,7 @@ export const readPdfContent = (file: File) => pageTextPromises.push(readPDFPage(doc, pageNo)); } const pageTexts = await Promise.all(pageTextPromises); - resolve(pageTexts.join('\n')); + resolve(pageTexts.join('')); } catch (err) { console.log(err, 'pdf load error'); reject('解析 PDF 失败'); diff --git a/projects/app/src/web/common/hooks/useEditTitle.tsx b/projects/app/src/web/common/hooks/useEditTitle.tsx index 51b902fff..66e4eb31c 100644 --- a/projects/app/src/web/common/hooks/useEditTitle.tsx +++ b/projects/app/src/web/common/hooks/useEditTitle.tsx @@ -51,7 +51,7 @@ export const useEditTitle = ({ // eslint-disable-next-line react/display-name const EditModal = useCallback( ({ maxLength = 30 }: { maxLength?: number }) => ( - + {!!tip && ( diff --git a/projects/app/src/web/common/system/staticData.ts b/projects/app/src/web/common/system/staticData.ts index a521450e2..80ff90340 100644 --- a/projects/app/src/web/common/system/staticData.ts +++ b/projects/app/src/web/common/system/staticData.ts @@ -11,6 +11,7 @@ import { defaultVectorModels, defaultAudioSpeechModels } from '@fastgpt/global/core/ai/model'; +import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type'; export let feConfigs: FeConfigsType = {}; export let priceMd = ''; @@ -23,6 +24,7 @@ export let cqModelList = defaultCQModels; export let extractModelList = defaultExtractModels; export let qgModelList = defaultQGModels; export let audioSpeechModels = defaultAudioSpeechModels; +export let simpleModeTemplates: AppSimpleEditConfigTemplateType[] = []; let retryTimes = 3; @@ -43,6 +45,8 @@ export const clientInitData = async (): Promise => { priceMd = res.priceMd; systemVersion = res.systemVersion; + simpleModeTemplates = res.simpleModeTemplates; + return res; } catch (error) { retryTimes--; diff --git a/projects/app/src/web/common/utils/voice.ts b/projects/app/src/web/common/utils/voice.ts index 5435a2d75..418ad6175 100644 --- a/projects/app/src/web/common/utils/voice.ts +++ b/projects/app/src/web/common/utils/voice.ts @@ -1,7 +1,7 @@ import { useState, useCallback, useEffect, useMemo, useRef } from 'react'; import { useToast } from '@/web/common/hooks/useToast'; import { getErrText } from '@fastgpt/global/common/error/utils'; -import { AppTTSConfigType } from '@/types/app'; +import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d'; import { TTSTypeEnum } from '@/constants/app'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; diff --git a/projects/app/src/web/core/app/basicSettings.ts b/projects/app/src/web/core/app/basicSettings.ts deleted file mode 100644 index d1e5732ba..000000000 --- a/projects/app/src/web/core/app/basicSettings.ts +++ /dev/null @@ -1,602 +0,0 @@ -import type { AppTTSConfigType, VariableItemType } from '@/types/app'; -import { chatModelList } from '@/web/common/system/staticData'; -import type { ModuleItemType } from '@fastgpt/global/core/module/type'; -import { - FlowNodeInputTypeEnum, - FlowNodeTypeEnum, - FlowNodeValTypeEnum, - FlowNodeSpecialInputKeyEnum -} from '@fastgpt/global/core/module/node/constant'; -import { SystemInputEnum } from '@/constants/app'; -import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d'; -import type { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type.d'; -import type { AIChatProps } from '@/types/core/aiChat'; -import { getGuideModule, splitGuideModule } from '@/global/core/app/modules/utils'; - -export type EditFormType = { - chatModel: AIChatProps; - dataset: { - list: SelectedDatasetType; - searchSimilarity: number; - searchLimit: number; - searchEmptyText: string; - rerank: boolean; - }; - guide: { - welcome: { - text: string; - }; - }; - variables: VariableItemType[]; - questionGuide: boolean; - tts: AppTTSConfigType; -}; -export const getDefaultAppForm = (): EditFormType => { - const defaultChatModel = chatModelList[0]; - - return { - chatModel: { - model: defaultChatModel?.model, - systemPrompt: '', - temperature: 0, - [SystemInputEnum.isResponseAnswerText]: true, - quotePrompt: '', - quoteTemplate: '', - maxToken: defaultChatModel ? defaultChatModel.maxResponse / 2 : 4000, - frequency: 0.5, - presence: -0.5 - }, - dataset: { - list: [], - searchSimilarity: 0.4, - searchLimit: 5, - searchEmptyText: '', - rerank: false - }, - guide: { - welcome: { - text: '' - } - }, - variables: [], - questionGuide: false, - tts: { - type: 'web' - } - }; -}; - -export const appModules2Form = (modules: ModuleItemType[]) => { - const defaultAppForm = getDefaultAppForm(); - const updateVal = ({ - formKey, - inputs, - key - }: { - formKey: string; - inputs: FlowNodeInputItemType[]; - 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 === FlowNodeTypeEnum.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 === FlowNodeTypeEnum.datasetSearchNode) { - updateVal({ - formKey: 'dataset.list', - inputs: module.inputs, - key: 'datasets' - }); - updateVal({ - formKey: 'dataset.searchSimilarity', - inputs: module.inputs, - key: 'similarity' - }); - updateVal({ - formKey: 'dataset.searchLimit', - inputs: module.inputs, - key: 'limit' - }); - updateVal({ - formKey: 'dataset.rerank', - inputs: module.inputs, - key: 'rerank' - }); - // 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.dataset.searchEmptyText = - target?.inputs?.find((item) => item.key === FlowNodeSpecialInputKeyEnum.answerText) - ?.value || ''; - } - } else if (module.flowType === FlowNodeTypeEnum.userGuide) { - const { welcomeText, variableModules, questionGuide, ttsConfig } = splitGuideModule( - getGuideModule(modules) - ); - if (welcomeText) { - defaultAppForm.guide.welcome = { - text: welcomeText - }; - } - - defaultAppForm.variables = variableModules; - defaultAppForm.questionGuide = !!questionGuide; - defaultAppForm.tts = ttsConfig; - } - }); - - return defaultAppForm; -}; - -const chatModelInput = (formData: EditFormType): FlowNodeInputItemType[] => [ - { - 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: SystemInputEnum.isResponseAnswerText, - value: true, - type: 'hidden', - label: '返回AI内容', - 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.dataset.list.length > 0 && !!formData.dataset.searchEmptyText - }, - { - key: 'quoteQA', - type: 'target', - label: '引用内容', - connected: formData.dataset.list.length > 0 - }, - { - key: 'history', - type: 'target', - label: '聊天记录', - connected: true - }, - { - key: 'userChatInput', - type: 'target', - label: '用户问题', - connected: true - } -]; -const userGuideTemplate = (formData: EditFormType): ModuleItemType[] => [ - { - name: '用户引导', - flowType: FlowNodeTypeEnum.userGuide, - inputs: [ - { - key: SystemInputEnum.welcomeText, - type: FlowNodeInputTypeEnum.hidden, - label: '开场白', - value: formData.guide.welcome.text - }, - { - key: SystemInputEnum.variables, - type: FlowNodeInputTypeEnum.hidden, - label: '对话框变量', - value: formData.variables - }, - { - key: SystemInputEnum.questionGuide, - type: FlowNodeInputTypeEnum.hidden, - label: '问题引导', - value: formData.questionGuide - }, - { - key: SystemInputEnum.tts, - type: FlowNodeInputTypeEnum.hidden, - label: '语音播报', - value: formData.tts - } - ], - outputs: [], - position: { - x: 447.98520778293346, - y: 721.4016845336229 - }, - moduleId: 'userGuide' - } -]; -const simpleChatTemplate = (formData: EditFormType): ModuleItemType[] => [ - { - name: '用户问题(对话入口)', - flowType: FlowNodeTypeEnum.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: FlowNodeTypeEnum.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: FlowNodeTypeEnum.chatNode, - inputs: chatModelInput(formData), - showStatus: true, - outputs: [ - { - key: 'answerText', - label: 'AI回复', - 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): ModuleItemType[] => [ - { - name: '用户问题(对话入口)', - flowType: FlowNodeTypeEnum.questionInput, - inputs: [ - { - key: 'userChatInput', - label: '用户问题', - type: 'target', - connected: true - } - ], - outputs: [ - { - key: 'userChatInput', - targets: [ - { - moduleId: 'chatModule', - key: 'userChatInput' - }, - { - moduleId: 'datasetSearch', - key: 'userChatInput' - } - ] - } - ], - position: { - x: 464.32198615344566, - y: 1602.2698463081606 - }, - moduleId: 'userChatInput' - }, - { - name: '聊天记录', - flowType: FlowNodeTypeEnum.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: FlowNodeTypeEnum.datasetSearchNode, - showStatus: true, - inputs: [ - { - key: 'datasets', - value: formData.dataset.list, - type: FlowNodeInputTypeEnum.custom, - label: '关联的知识库', - connected: true - }, - { - key: 'similarity', - value: formData.dataset.searchSimilarity, - type: FlowNodeInputTypeEnum.slider, - label: '相似度', - connected: true - }, - { - key: 'limit', - value: formData.dataset.searchLimit, - type: FlowNodeInputTypeEnum.slider, - label: '单次搜索上限', - connected: true - }, - { - key: 'switch', - type: FlowNodeInputTypeEnum.target, - label: '触发器', - connected: false - }, - { - key: 'userChatInput', - type: FlowNodeInputTypeEnum.target, - label: '用户问题', - connected: true - }, - { - key: 'rerank', - type: FlowNodeInputTypeEnum.switch, - label: '结果重排', - description: '将召回的结果进行进一步重排,可增加召回率', - plusField: true, - connected: true, - value: formData.dataset.rerank - } - ], - outputs: [ - { - key: 'isEmpty', - targets: formData.dataset.searchEmptyText - ? [ - { - moduleId: 'emptyText', - key: 'switch' - } - ] - : [] - }, - { - key: 'unEmpty', - targets: formData.dataset.searchEmptyText - ? [ - { - moduleId: 'chatModule', - key: 'switch' - } - ] - : [] - }, - { - key: 'quoteQA', - targets: [ - { - moduleId: 'chatModule', - key: 'quoteQA' - } - ] - } - ], - position: { - x: 956.0838440206068, - y: 887.462827870246 - }, - moduleId: 'datasetSearch' - }, - ...(formData.dataset.searchEmptyText - ? [ - { - name: '指定回复', - flowType: FlowNodeTypeEnum.answerNode, - inputs: [ - { - key: 'switch', - type: FlowNodeInputTypeEnum.target, - label: '触发器', - connected: true - }, - { - key: FlowNodeSpecialInputKeyEnum.answerText, - value: formData.dataset.searchEmptyText, - type: FlowNodeInputTypeEnum.textarea, - valueType: FlowNodeValTypeEnum.string, - label: '回复的内容', - connected: true - } - ], - outputs: [], - position: { - x: 1553.5815811529146, - y: 637.8753731306779 - }, - moduleId: 'emptyText' - } - ] - : []), - { - name: 'AI 对话', - flowType: FlowNodeTypeEnum.chatNode, - inputs: chatModelInput(formData), - showStatus: true, - outputs: [ - { - key: 'answerText', - label: 'AI回复', - 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.dataset.list.length > 0 ? kbTemplate(formData) : simpleChatTemplate(formData)) - ]; - - return modules as ModuleItemType[]; -}; diff --git a/projects/app/src/constants/flow/ModuleTemplate.ts b/projects/app/src/web/core/app/templates.ts similarity index 68% rename from projects/app/src/constants/flow/ModuleTemplate.ts rename to projects/app/src/web/core/app/templates.ts index 461065d38..17ae425f9 100644 --- a/projects/app/src/constants/flow/ModuleTemplate.ts +++ b/projects/app/src/web/core/app/templates.ts @@ -1,651 +1,5 @@ -import { SystemInputEnum } from '../app'; +import { AppItemType } from '@/types/app'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; -import { TaskResponseKeyEnum } from '@fastgpt/global/core/chat/constants'; -import { - FlowNodeTypeEnum, - FlowNodeInputTypeEnum, - FlowNodeOutputTypeEnum, - FlowNodeSpecialInputKeyEnum, - FlowNodeValTypeEnum -} from '@fastgpt/global/core/module/node/constant'; -import type { AppItemType } from '@/types/app'; -import type { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; -import { chatModelList, cqModelList } from '@/web/common/system/staticData'; -import { - Input_Template_History, - Input_Template_TFSwitch, - Input_Template_UserChatInput -} from './inputTemplate'; -import { ContextExtractEnum, HttpPropsEnum } from './flowField'; -import { Output_Template_Finish } from './outputTemplate'; - -export const ChatModelSystemTip = - '模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}'; -export const ChatModelLimitTip = - '限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。不建议内容太长,会影响上下文,可使用变量,例如 {{language}}。可在文档中找到对应的限定例子'; -export const userGuideTip = '可以在对话前设置引导语,设置全局变量,设置下一步指引'; -export const welcomeTextTip = - '每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]: 用户点击后可以直接发送该问题'; -export const variableTip = - '可以在对话开始前,要求用户填写一些内容作为本轮对话的特定变量。该模块位于开场引导之后。\n变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等'; - -export const VariableModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.variable, - flowType: FlowNodeTypeEnum.variable, - logo: '/imgs/module/variable.png', - name: '全局变量', - intro: variableTip, - description: - '全局变量可以通过 {{变量key}} 的形式注入到其他模块 string 类型的输入中,例如:提示词、限定词等', - inputs: [ - { - key: SystemInputEnum.variables, - type: FlowNodeInputTypeEnum.systemInput, - label: '变量输入', - value: [] - } - ], - outputs: [] -}; -export const UserGuideModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.userGuide, - flowType: FlowNodeTypeEnum.userGuide, - logo: '/imgs/module/userGuide.png', - name: '用户引导', - intro: userGuideTip, - inputs: [ - { - key: SystemInputEnum.welcomeText, - type: FlowNodeInputTypeEnum.hidden, - label: '开场白' - }, - { - key: SystemInputEnum.variables, - type: FlowNodeInputTypeEnum.hidden, - label: '对话框变量', - value: [] - }, - { - key: SystemInputEnum.questionGuide, - type: FlowNodeInputTypeEnum.switch, - label: '问题引导' - }, - { - key: SystemInputEnum.tts, - type: FlowNodeInputTypeEnum.hidden, - label: '语音播报' - } - ], - outputs: [] -}; -export const UserInputModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.questionInput, - flowType: FlowNodeTypeEnum.questionInput, - logo: '/imgs/module/userChatInput.png', - name: '用户问题(对话入口)', - intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', - inputs: [ - { - key: SystemInputEnum.userChatInput, - type: FlowNodeInputTypeEnum.systemInput, - label: '用户问题' - } - ], - outputs: [ - { - key: SystemInputEnum.userChatInput, - label: '用户问题', - type: FlowNodeOutputTypeEnum.source, - valueType: FlowNodeValTypeEnum.string, - targets: [] - } - ] -}; -export const HistoryModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.historyNode, - flowType: FlowNodeTypeEnum.historyNode, - logo: '/imgs/module/history.png', - name: '聊天记录', - intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', - inputs: [ - { - key: 'maxContext', - type: FlowNodeInputTypeEnum.numberInput, - label: '最长记录数', - value: 6, - min: 0, - max: 50 - }, - { - key: SystemInputEnum.history, - type: FlowNodeInputTypeEnum.hidden, - label: '聊天记录' - } - ], - outputs: [ - { - key: SystemInputEnum.history, - label: '聊天记录', - valueType: FlowNodeValTypeEnum.chatHistory, - type: FlowNodeOutputTypeEnum.source, - targets: [] - } - ] -}; -export const ChatModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.chatNode, - flowType: FlowNodeTypeEnum.chatNode, - logo: '/imgs/module/AI.png', - name: 'AI 对话', - intro: 'AI 大模型对话', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: 'model', - type: FlowNodeInputTypeEnum.selectChatModel, - label: '对话模型', - value: chatModelList?.[0]?.model, - customData: () => chatModelList, - required: true, - valueCheck: (val) => !!val - }, - { - key: 'temperature', - type: FlowNodeInputTypeEnum.hidden, - label: '温度', - value: 0, - min: 0, - max: 10, - step: 1, - markList: [ - { label: '严谨', value: 0 }, - { label: '发散', value: 10 } - ] - }, - { - key: 'maxToken', - type: FlowNodeInputTypeEnum.hidden, - label: '回复上限', - value: chatModelList?.[0] ? chatModelList[0].maxResponse / 2 : 2000, - min: 100, - max: chatModelList?.[0]?.maxResponse || 4000, - step: 50, - markList: [ - { label: '100', value: 100 }, - { - label: `${chatModelList?.[0]?.maxResponse || 4000}`, - value: chatModelList?.[0]?.maxResponse || 4000 - } - ] - }, - { - key: 'aiSettings', - type: FlowNodeInputTypeEnum.aiSettings, - label: '', - connected: false - }, - { - key: 'systemPrompt', - type: FlowNodeInputTypeEnum.textarea, - label: '系统提示词', - max: 300, - valueType: FlowNodeValTypeEnum.string, - description: ChatModelSystemTip, - placeholder: ChatModelSystemTip, - value: '' - }, - { - key: SystemInputEnum.isResponseAnswerText, - type: FlowNodeInputTypeEnum.hidden, - label: '返回AI内容', - valueType: FlowNodeValTypeEnum.boolean, - value: true - }, - { - key: 'quoteTemplate', - type: FlowNodeInputTypeEnum.hidden, - label: '引用内容模板', - valueType: FlowNodeValTypeEnum.string, - value: '' - }, - { - key: 'quotePrompt', - type: FlowNodeInputTypeEnum.hidden, - label: '引用内容提示词', - valueType: FlowNodeValTypeEnum.string, - value: '' - }, - { - key: 'quoteQA', - type: FlowNodeInputTypeEnum.target, - label: '引用内容', - description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - valueType: FlowNodeValTypeEnum.datasetQuote, - connected: false - }, - Input_Template_History, - Input_Template_UserChatInput - ], - outputs: [ - { - key: TaskResponseKeyEnum.history, - label: '新的上下文', - description: '将本次回复内容拼接上历史记录,作为新的上下文返回', - valueType: FlowNodeValTypeEnum.chatHistory, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - { - key: TaskResponseKeyEnum.answerText, - label: 'AI回复', - description: '将在 stream 回复完毕后触发', - valueType: FlowNodeValTypeEnum.string, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - Output_Template_Finish - ] -}; -export const DatasetSearchModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.datasetSearchNode, - flowType: FlowNodeTypeEnum.datasetSearchNode, - logo: '/imgs/module/db.png', - name: '知识库搜索', - intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: 'datasets', - type: FlowNodeInputTypeEnum.selectDataset, - label: '关联的知识库', - value: [], - list: [], - required: true, - valueCheck: (val) => !!val.length - }, - { - key: 'similarity', - type: FlowNodeInputTypeEnum.slider, - label: '相似度', - value: 0.4, - min: 0, - max: 1, - step: 0.01, - markList: [ - { label: '100', value: 100 }, - { label: '1', value: 1 } - ] - }, - { - key: 'limit', - type: FlowNodeInputTypeEnum.slider, - label: '单次搜索上限', - description: '最多取 n 条记录作为本次问题引用', - value: 5, - min: 1, - max: 20, - step: 1, - markList: [ - { label: '1', value: 1 }, - { label: '20', value: 20 } - ] - }, - { - key: 'rerank', - type: FlowNodeInputTypeEnum.switch, - label: '结果重排', - description: '将召回的结果进行进一步重排,可增加召回率', - plusField: true, - value: false - }, - Input_Template_UserChatInput - ], - outputs: [ - { - key: 'isEmpty', - label: '搜索结果为空', - type: FlowNodeOutputTypeEnum.source, - valueType: FlowNodeValTypeEnum.boolean, - targets: [] - }, - { - key: 'unEmpty', - label: '搜索结果不为空', - type: FlowNodeOutputTypeEnum.source, - valueType: FlowNodeValTypeEnum.boolean, - targets: [] - }, - { - key: 'quoteQA', - label: '引用内容', - description: - '始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器', - type: FlowNodeOutputTypeEnum.source, - valueType: FlowNodeValTypeEnum.datasetQuote, - targets: [] - }, - Output_Template_Finish - ] -}; -export const AnswerModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.answerNode, - flowType: FlowNodeTypeEnum.answerNode, - logo: '/imgs/module/reply.png', - name: '指定回复', - intro: '该模块可以直接回复一段指定的内容。常用于引导、提示', - description: '该模块可以直接回复一段指定的内容。常用于引导、提示', - inputs: [ - Input_Template_TFSwitch, - { - key: FlowNodeSpecialInputKeyEnum.answerText, - type: FlowNodeInputTypeEnum.textarea, - valueType: FlowNodeValTypeEnum.any, - value: '', - label: '回复的内容', - description: - '可以使用 \\n 来实现连续换行。\n\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n\n如传入非字符串类型数据将会自动转成字符串' - } - ], - outputs: [Output_Template_Finish] -}; -export const ClassifyQuestionModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.classifyQuestion, - flowType: FlowNodeTypeEnum.classifyQuestion, - logo: '/imgs/module/cq.png', - name: '问题分类', - intro: '可以判断用户问题属于哪方面问题,从而执行不同的操作。', - description: - '根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于 laf 通用问题\n类型3: 关于 laf 代码问题\n类型4: 其他问题', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: 'model', - type: FlowNodeInputTypeEnum.selectChatModel, - label: '分类模型', - value: cqModelList?.[0]?.model, - customData: () => cqModelList, - required: true, - valueCheck: (val) => !!val - }, - { - key: 'systemPrompt', - type: FlowNodeInputTypeEnum.textarea, - valueType: FlowNodeValTypeEnum.string, - value: '', - label: '背景知识', - description: - '你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。', - placeholder: '例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统' - }, - Input_Template_History, - Input_Template_UserChatInput, - { - key: FlowNodeSpecialInputKeyEnum.agents, - type: FlowNodeInputTypeEnum.custom, - label: '', - value: [ - { - value: '打招呼', - key: 'fasw' - }, - { - value: '关于 xxx 的问题', - key: 'fqsw' - }, - { - value: '其他问题', - key: 'fesw' - } - ] - } - ], - outputs: [ - { - key: 'fasw', - label: '', - type: FlowNodeOutputTypeEnum.hidden, - targets: [] - }, - { - key: 'fqsw', - label: '', - type: FlowNodeOutputTypeEnum.hidden, - targets: [] - }, - { - key: 'fesw', - label: '', - type: FlowNodeOutputTypeEnum.hidden, - targets: [] - } - ] -}; -export const ContextExtractModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.contentExtract, - flowType: FlowNodeTypeEnum.contentExtract, - logo: '/imgs/module/extract.png', - name: '文本内容提取', - intro: '从文本中提取出指定格式的数据', - description: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: ContextExtractEnum.description, - type: FlowNodeInputTypeEnum.textarea, - valueType: FlowNodeValTypeEnum.string, - value: '', - label: '提取要求描述', - description: '写一段提取要求,告诉 AI 需要提取哪些内容', - required: true, - placeholder: '例如: \n1. 你是一个实验室预约助手。根据用户问题,提取出姓名、实验室号和预约时间' - }, - Input_Template_History, - { - key: ContextExtractEnum.content, - type: FlowNodeInputTypeEnum.target, - label: '需要提取的文本', - required: true, - valueType: FlowNodeValTypeEnum.string - }, - { - key: ContextExtractEnum.extractKeys, - type: FlowNodeInputTypeEnum.custom, - label: '目标字段', - description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段", - value: [] - } - ], - outputs: [ - { - key: ContextExtractEnum.success, - label: '字段完全提取', - valueType: FlowNodeValTypeEnum.boolean, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - { - key: ContextExtractEnum.failed, - label: '提取字段缺失', - valueType: FlowNodeValTypeEnum.boolean, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - { - key: ContextExtractEnum.fields, - label: '完整提取结果', - description: '一个 JSON 字符串,例如:{"name:":"YY","Time":"2023/7/2 18:00"}', - valueType: FlowNodeValTypeEnum.string, - type: FlowNodeOutputTypeEnum.source, - targets: [] - } - ] -}; -export const HttpModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.httpRequest, - flowType: FlowNodeTypeEnum.httpRequest, - logo: '/imgs/module/http.png', - name: 'HTTP模块', - intro: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)', - description: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: HttpPropsEnum.url, - value: '', - type: FlowNodeInputTypeEnum.input, - label: '请求地址', - description: '请求目标地址', - placeholder: 'https://api.fastgpt.run/getInventory', - required: true, - valueCheck: (val) => !!val - } - ], - outputs: [Output_Template_Finish] -}; -export const EmptyModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.empty, - flowType: FlowNodeTypeEnum.empty, - logo: '/imgs/module/cq.png', - name: '该模块已被移除', - intro: '', - description: '', - inputs: [], - outputs: [] -}; -export const RunAppModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.runApp, - flowType: FlowNodeTypeEnum.runApp, - logo: '/imgs/module/app.png', - name: '应用调用', - intro: '可以选择一个其他应用进行调用', - description: '可以选择一个其他应用进行调用', - showStatus: true, - inputs: [ - Input_Template_TFSwitch, - { - key: 'app', - type: FlowNodeInputTypeEnum.selectApp, - label: '选择一个应用', - description: '选择一个其他应用进行调用', - required: true - }, - Input_Template_History, - Input_Template_UserChatInput - ], - outputs: [ - { - key: TaskResponseKeyEnum.history, - label: '新的上下文', - description: '将该应用回复内容拼接到历史记录中,作为新的上下文返回', - valueType: FlowNodeValTypeEnum.chatHistory, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - { - key: TaskResponseKeyEnum.answerText, - label: 'AI回复', - description: '将在应用完全结束后触发', - valueType: FlowNodeValTypeEnum.string, - type: FlowNodeOutputTypeEnum.source, - targets: [] - }, - Output_Template_Finish - ] -}; -export const PluginInputModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.pluginInput, - flowType: FlowNodeTypeEnum.pluginInput, - logo: '/imgs/module/input.png', - name: '定义插件输入', - intro: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入', - description: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入', - showStatus: false, - inputs: [], - outputs: [] -}; -export const PluginOutputModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.pluginOutput, - flowType: FlowNodeTypeEnum.pluginOutput, - logo: '/imgs/module/output.png', - name: '定义插件输出', - intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出', - description: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出', - showStatus: false, - inputs: [], - outputs: [] -}; -export const PluginModule: FlowModuleTemplateType = { - id: FlowNodeTypeEnum.pluginModule, - flowType: FlowNodeTypeEnum.pluginModule, - logo: '/imgs/module/custom.png', - name: '自定义模块', - showStatus: false, - inputs: [], - outputs: [] -}; - -export const SystemModuleTemplates = [ - { - label: '引导模块', - list: [UserGuideModule] - }, - { - label: '输入模块', - list: [UserInputModule, HistoryModule] - }, - { - label: '内容生成', - list: [ChatModule, AnswerModule] - }, - { - label: '核心调用', - list: [DatasetSearchModule, RunAppModule] - }, - { - label: '函数模块', - list: [ClassifyQuestionModule, ContextExtractModule, HttpModule] - } -]; -export const PluginModuleTemplates = [ - { - label: '输入输出', - list: [PluginInputModule, PluginOutputModule, HistoryModule] - }, - { - label: '内容生成', - list: [ChatModule, AnswerModule] - }, - { - label: '核心调用', - list: [DatasetSearchModule, RunAppModule] - }, - { - label: '函数模块', - list: [ClassifyQuestionModule, ContextExtractModule, HttpModule] - } -]; -export const ModuleTemplatesFlat = [ - VariableModule, - UserGuideModule, - UserInputModule, - HistoryModule, - ChatModule, - DatasetSearchModule, - AnswerModule, - ClassifyQuestionModule, - ContextExtractModule, - HttpModule, - EmptyModule, - RunAppModule, - PluginInputModule, - PluginOutputModule, - PluginModule -]; // template export const appTemplates: (AppItemType & { @@ -658,7 +12,7 @@ export const appTemplates: (AppItemType & { avatar: '/imgs/module/AI.png', name: '简单的对话', intro: '一个极其简单的 AI 对话应用', - type: AppTypeEnum.basic, + type: AppTypeEnum.simple, modules: [ { moduleId: 'userGuide', @@ -875,7 +229,7 @@ export const appTemplates: (AppItemType & { avatar: '/imgs/module/db.png', name: '知识库 + 对话引导', intro: '每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答', - type: AppTypeEnum.basic, + type: AppTypeEnum.simple, modules: [ { moduleId: 'userGuide', @@ -1241,7 +595,7 @@ export const appTemplates: (AppItemType & { avatar: '/imgs/module/userGuide.png', name: '对话引导 + 变量', intro: '可以在对话开始发送一段提示,或者让用户填写一些内容,作为本次对话的变量', - type: AppTypeEnum.basic, + type: AppTypeEnum.simple, modules: [ { moduleId: 'userGuide', diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts new file mode 100644 index 000000000..8fc5d93ed --- /dev/null +++ b/projects/app/src/web/core/app/utils.ts @@ -0,0 +1,65 @@ +import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; +import { ModuleItemType } from '@fastgpt/global/core/module/type'; +import { POST } from '@/web/common/api/request'; +import { chatModelList } from '@/web/common/system/staticData'; +import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import type { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api.d'; + +export async function postForm2Modules( + data: AppSimpleEditFormType, + templateId = 'fastgpt-universal' +) { + function userGuideTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { + return [ + { + name: '用户引导', + flowType: FlowNodeTypeEnum.userGuide, + inputs: [ + { + key: ModuleInputKeyEnum.welcomeText, + type: FlowNodeInputTypeEnum.hidden, + label: '开场白', + value: formData.userGuide.welcomeText + }, + { + key: ModuleInputKeyEnum.variables, + type: FlowNodeInputTypeEnum.hidden, + label: '对话框变量', + value: formData.userGuide.variables + }, + { + key: ModuleInputKeyEnum.questionGuide, + type: FlowNodeInputTypeEnum.hidden, + label: '问题引导', + value: formData.userGuide.questionGuide + }, + { + key: ModuleInputKeyEnum.tts, + type: FlowNodeInputTypeEnum.hidden, + label: '语音播报', + value: formData.userGuide.tts + } + ], + outputs: [], + position: { + x: 447.98520778293346, + y: 721.4016845336229 + }, + moduleId: 'userGuide' + } + ]; + } + const maxToken = + chatModelList.find((item) => item.model === data.aiSettings.model)?.maxResponse || 4000; + + const props: FormatForm2ModulesProps = { + formData: data, + chatModelMaxToken: maxToken, + chatModelList + }; + + const modules = await POST(`/core/app/form2Modules/${templateId}`, props); + + return [...userGuideTemplate(data), ...modules]; +} diff --git a/projects/app/src/web/core/dataset/components/SelectCollections.tsx b/projects/app/src/web/core/dataset/components/SelectCollections.tsx index cd75b413d..77a499fba 100644 --- a/projects/app/src/web/core/dataset/components/SelectCollections.tsx +++ b/projects/app/src/web/core/dataset/components/SelectCollections.tsx @@ -104,98 +104,99 @@ const SelectCollections = ({ w={'100%'} h={['90vh', '80vh']} isCentered + iconSrc="/imgs/modal/move.svg" + title={ + + ({ + parentId: path.parentId, + parentName: path.parentName + }))} + FirstPathDom={ + <> + + {title || type === 'folder' + ? t('common.Select One Folder') + : t('dataset.collections.Select Collection')} + + {!!tip && ( + + {tip} + + )} + + } + onClick={(e) => { + setParentId(e); + }} + /> + + } > - - - - ({ - parentId: path.parentId, - parentName: path.parentName - }))} - FirstPathDom={ - <> - - {title || type === 'folder' - ? t('common.Select One Folder') - : t('dataset.collections.Select Collection')} - - {!!tip && ( - - {tip} + + + {collections.map((item) => + (() => { + const selected = selectedDatasetCollectionIds.includes(item._id); + return ( + { + if (item.type === DatasetCollectionTypeEnum.folder) { + setParentId(item._id); + } else { + let result: string[] = []; + if (max === 1) { + result = [item._id]; + } else if (selected) { + result = selectedDatasetCollectionIds.filter((id) => id !== item._id); + } else if (selectedDatasetCollectionIds.length < max) { + result = [...selectedDatasetCollectionIds, item._id]; + } + setSelectedDatasetCollectionIds(result); + onChange && onChange({ parentId, collectionIds: result }); + } + }} + > + + {''} + + {item.name} - )} - - } - onClick={(e) => { - setParentId(e); - }} - /> - - - - {collections.map((item) => - (() => { - const selected = selectedDatasetCollectionIds.includes(item._id); - return ( - { - if (item.type === DatasetCollectionTypeEnum.folder) { - setParentId(item._id); - } else { - let result: string[] = []; - if (max === 1) { - result = [item._id]; - } else if (selected) { - result = selectedDatasetCollectionIds.filter((id) => id !== item._id); - } else if (selectedDatasetCollectionIds.length < max) { - result = [...selectedDatasetCollectionIds, item._id]; - } - setSelectedDatasetCollectionIds(result); - onChange && onChange({ parentId, collectionIds: result }); - } - }} - > - - {''} - - {item.name} - - - - ); - })() - )} - - {collections.length === 0 && ( - - - - {t('common.folder.No Folder')} - - - )} - - - + + + ); + })() + )} + + {collections.length === 0 && ( + + + + {t('common.folder.No Folder')} + + + )} + {CustomFooter ? ( <>{CustomFooter} diff --git a/projects/app/src/web/core/modules/constants/dataType.ts b/projects/app/src/web/core/modules/constants/dataType.ts new file mode 100644 index 000000000..3110d5096 --- /dev/null +++ b/projects/app/src/web/core/modules/constants/dataType.ts @@ -0,0 +1,82 @@ +import type { BoxProps } from '@chakra-ui/react'; +import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants'; + +export const FlowValueTypeStyle: Record<`${ModuleDataTypeEnum}`, BoxProps> = { + [ModuleDataTypeEnum.string]: { + background: '#36ADEF' + }, + [ModuleDataTypeEnum.number]: { + background: '#FB7C3C' + }, + [ModuleDataTypeEnum.boolean]: { + background: '#E7D118' + }, + [ModuleDataTypeEnum.chatHistory]: { + background: '#00A9A6' + }, + [ModuleDataTypeEnum.datasetQuote]: { + background: '#A558C9' + }, + [ModuleDataTypeEnum.any]: { + background: '#9CA2A8' + }, + [ModuleDataTypeEnum.selectApp]: { + background: '#6a6efa' + }, + [ModuleDataTypeEnum.selectDataset]: { + background: '#21ba45' + } +}; +export const FlowValueTypeMap = { + [ModuleDataTypeEnum.string]: { + label: 'core.module.valueType.string', + value: ModuleDataTypeEnum.string, + example: '' + }, + [ModuleDataTypeEnum.number]: { + label: 'core.module.valueType.number', + value: ModuleDataTypeEnum.number, + example: '' + }, + [ModuleDataTypeEnum.boolean]: { + label: 'core.module.valueType.boolean', + value: ModuleDataTypeEnum.boolean, + example: '' + }, + [ModuleDataTypeEnum.chatHistory]: { + label: 'core.module.valueType.chatHistory', + value: ModuleDataTypeEnum.chatHistory, + example: `{ + obj: System | Human | AI; + value: string; +}` + }, + [ModuleDataTypeEnum.datasetQuote]: { + label: 'core.module.valueType.datasetQuote', + value: ModuleDataTypeEnum.datasetQuote, + example: `{ + id: string; + datasetId: string; + collectionId: string; + sourceName: string; + sourceId?: string; + q: string; + a: string +}` + }, + [ModuleDataTypeEnum.any]: { + label: 'core.module.valueType.any', + value: ModuleDataTypeEnum.any, + example: '' + }, + [ModuleDataTypeEnum.selectApp]: { + label: 'core.module.valueType.selectApp', + value: ModuleDataTypeEnum.selectApp, + example: '' + }, + [ModuleDataTypeEnum.selectDataset]: { + label: 'core.module.valueType.selectDataset', + value: ModuleDataTypeEnum.selectDataset, + example: '' + } +}; diff --git a/projects/app/src/web/core/modules/constants/flowUi.ts b/projects/app/src/web/core/modules/constants/flowUi.ts new file mode 100644 index 000000000..13347f684 --- /dev/null +++ b/projects/app/src/web/core/modules/constants/flowUi.ts @@ -0,0 +1,7 @@ +export const edgeOptions = { + style: { + strokeWidth: 1.5, + stroke: '#5A646Es' + } +}; +export const connectionLineStyle = { strokeWidth: 1.5, stroke: '#5A646Es' }; diff --git a/projects/app/src/web/core/modules/template/system.ts b/projects/app/src/web/core/modules/template/system.ts new file mode 100644 index 000000000..39fd0097b --- /dev/null +++ b/projects/app/src/web/core/modules/template/system.ts @@ -0,0 +1,153 @@ +import { UserGuideModule } from '@fastgpt/global/core/module/template/system/userGuide'; +import { UserInputModule } from '@fastgpt/global/core/module/template/system/userInput'; +import { HistoryModule } from '@fastgpt/global/core/module/template/system/history'; +import { AiChatModule } from '@fastgpt/global/core/module/template/system/aiChat'; +import { DatasetSearchModule } from '@fastgpt/global/core/module/template/system/datasetSearch'; +import { AssignedAnswerModule } from '@fastgpt/global/core/module/template/system/assignedAnswer'; +import { ClassifyQuestionModule } from '@fastgpt/global/core/module/template/system/classifyQuestion'; +import { ContextExtractModule } from '@fastgpt/global/core/module/template/system/contextExtract'; +import { HttpModule } from '@fastgpt/global/core/module/template/system/http'; +import { EmptyModule } from '@fastgpt/global/core/module/template/system/empty'; +import { RunAppModule } from '@fastgpt/global/core/module/template/system/runApp'; +import { PluginInputModule } from '@fastgpt/global/core/module/template/system/pluginInput'; +import { PluginOutputModule } from '@fastgpt/global/core/module/template/system/pluginOutput'; +import { RunPluginModule } from '@fastgpt/global/core/module/template/system/runPlugin'; +import type { + FlowModuleTemplateType, + moduleTemplateListType +} from '@fastgpt/global/core/module/type.d'; +import { ModuleTemplateTypeEnum } from '@fastgpt/global/core/module/constants'; + +export const appSystemModuleTemplates: FlowModuleTemplateType[] = [ + UserGuideModule, + UserInputModule, + HistoryModule, + AiChatModule, + AssignedAnswerModule, + DatasetSearchModule, + RunAppModule, + ClassifyQuestionModule, + ContextExtractModule, + HttpModule +]; +export const pluginSystemModuleTemplates: FlowModuleTemplateType[] = [ + PluginInputModule, + PluginOutputModule, + HistoryModule, + AiChatModule, + AssignedAnswerModule, + DatasetSearchModule, + RunAppModule, + ClassifyQuestionModule, + ContextExtractModule, + HttpModule +]; +export const moduleTemplatesFlat: FlowModuleTemplateType[] = [ + UserGuideModule, + UserInputModule, + HistoryModule, + AiChatModule, + DatasetSearchModule, + AssignedAnswerModule, + ClassifyQuestionModule, + ContextExtractModule, + HttpModule, + EmptyModule, + RunAppModule, + PluginInputModule, + PluginOutputModule, + RunPluginModule +]; + +export const moduleTemplatesList: moduleTemplateListType = [ + { + type: ModuleTemplateTypeEnum.userGuide, + label: '引导模块', + list: [] + }, + { + type: ModuleTemplateTypeEnum.systemInput, + label: '系统输入', + list: [] + }, + { + type: ModuleTemplateTypeEnum.textAnswer, + label: '文本输出', + list: [] + }, + { + type: ModuleTemplateTypeEnum.dataset, + label: '知识库', + list: [] + }, + { + type: ModuleTemplateTypeEnum.functionCall, + label: '函数调用', + list: [] + }, + { + type: ModuleTemplateTypeEnum.externalCall, + label: '外部调用', + list: [] + }, + { + type: ModuleTemplateTypeEnum.personalPlugin, + label: '个人插件', + list: [] + }, + { + type: ModuleTemplateTypeEnum.communityPlugin, + label: '社区插件', + list: [] + }, + { + type: ModuleTemplateTypeEnum.commercialPlugin, + label: '商业插件', + list: [] + }, + { + type: ModuleTemplateTypeEnum.other, + label: '其他', + list: [] + } +]; +// export const appSystemModuleTemplates = [ +// { +// label: '引导模块', +// list: [UserGuideModule] +// }, +// { +// label: '输入模块', +// list: [UserInputModule, HistoryModule] +// }, +// { +// label: '内容生成', +// list: [AiChatModule, AssignedAnswerModule] +// }, +// { +// label: '核心调用', +// list: [DatasetSearchModule, RunAppModule] +// }, +// { +// label: '函数模块', +// list: [ClassifyQuestionModule, ContextExtractModule, HttpModule] +// } +// ]; +// export const pluginModuleTemplates = [ +// { +// label: '输入输出', +// list: [PluginInputModule, PluginOutputModule, HistoryModule] +// }, +// { +// label: '内容生成', +// list: [AiChatModule, AssignedAnswerModule] +// }, +// { +// label: '核心调用', +// list: [DatasetSearchModule, RunAppModule] +// }, +// { +// label: '函数模块', +// list: [ClassifyQuestionModule, ContextExtractModule, HttpModule] +// } +// ]; diff --git a/projects/app/src/web/core/plugin/api.ts b/projects/app/src/web/core/plugin/api.ts index 165fc9aa1..b244c9fd2 100644 --- a/projects/app/src/web/core/plugin/api.ts +++ b/projects/app/src/web/core/plugin/api.ts @@ -11,9 +11,8 @@ export const postCreatePlugin = (data: CreateOnePluginParams) => POST('/core/plugin/create', data); export const putUpdatePlugin = (data: UpdatePluginParams) => PUT('/core/plugin/update', data); export const getUserPlugins = () => GET('/core/plugin/list'); -export const getUserPlugs2ModuleTemplates = () => - GET('/core/plugin/templateList'); -export const getPluginModuleDetail = (id: string) => - GET('/core/plugin/moduleDetail', { id }); +export const getPlugTemplates = () => GET('/core/plugin/templates'); +export const getPreviewPluginModule = (id: string) => + GET('/core/plugin/getPreviewModule', { id }); export const getOnePlugin = (id: string) => GET('/core/plugin/detail', { id }); export const delOnePlugin = (id: string) => DELETE('/core/plugin/delete', { id }); diff --git a/projects/app/src/web/core/plugin/store/plugin.ts b/projects/app/src/web/core/plugin/store/plugin.ts index 63290293c..c3b40504d 100644 --- a/projects/app/src/web/core/plugin/store/plugin.ts +++ b/projects/app/src/web/core/plugin/store/plugin.ts @@ -1,23 +1,23 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; -import type { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; -import { getUserPlugs2ModuleTemplates } from '../api'; +import { getPlugTemplates } from '../api'; +import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; type State = { pluginModuleTemplates: FlowModuleTemplateType[]; - loadPluginModuleTemplates: (init?: boolean) => Promise; + loadPluginTemplates: (init?: boolean) => Promise; }; export const usePluginStore = create()( devtools( immer((set, get) => ({ pluginModuleTemplates: [], - async loadPluginModuleTemplates(init) { + async loadPluginTemplates(init) { if (!init && get().pluginModuleTemplates.length > 0) { return get().pluginModuleTemplates; } - const templates = await getUserPlugs2ModuleTemplates(); + const templates = await getPlugTemplates(); set((state) => { state.pluginModuleTemplates = templates; }); diff --git a/projects/app/src/web/support/user/api.ts b/projects/app/src/web/support/user/api.ts index 371e384fe..36b09b79b 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -4,7 +4,11 @@ import type { ResLogin } from '@/global/support/api/userRes.d'; import { UserAuthTypeEnum } from '@/constants/common'; import { UserUpdateParams } from '@/types/user'; import { UserType } from '@fastgpt/global/support/user/type.d'; -import type { OauthLoginProps, PostLoginProps } from '@fastgpt/global/support/user/api.d'; +import type { + FastLoginProps, + OauthLoginProps, + PostLoginProps +} from '@fastgpt/global/support/user/api.d'; export const sendAuthCode = (data: { username: string; @@ -16,6 +20,8 @@ export const getTokenLogin = () => GET('/user/account/tokenLogin', {}, { maxQuantity: 1 }); export const oauthLogin = (params: OauthLoginProps) => POST('/plusApi/support/user/account/login/oauth', params); +export const postFastLogin = (params: FastLoginProps) => + POST('/plusApi/support/user/account/login/fastLogin', params); export const postRegister = ({ username,