diff --git a/docSite/content/docs/course/webSync.md b/docSite/content/docs/course/websync.md similarity index 100% rename from docSite/content/docs/course/webSync.md rename to docSite/content/docs/course/websync.md diff --git a/docSite/content/docs/development/openapi/share.md b/docSite/content/docs/development/openapi/share.md index 4a8aa4f24..93df0f216 100644 --- a/docSite/content/docs/development/openapi/share.md +++ b/docSite/content/docs/development/openapi/share.md @@ -24,7 +24,9 @@ weight: 860 "success": true, "message": "错误提示", "msg": "同message, 错误提示", - "uid": "用户唯一凭证" + "data": { + "uid": "用户唯一凭证" + } } ``` @@ -80,7 +82,9 @@ curl --location --request POST '{{host}}/shareAuth/init' \ ```json { "success": true, - "uid": "username123", + "data": { + "uid": "用户唯一凭证" + } } ``` @@ -129,7 +133,9 @@ curl --location --request POST '{{host}}/shareAuth/start' \ ```json { "success": true, - "uid": "username123", + "data": { + "uid": "用户唯一凭证" + } } ``` diff --git a/docSite/content/docs/development/upgrading/464.md b/docSite/content/docs/development/upgrading/464.md index 404e5c458..b93b3257d 100644 --- a/docSite/content/docs/development/upgrading/464.md +++ b/docSite/content/docs/development/upgrading/464.md @@ -32,7 +32,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv464' \ 4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。 5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块 6. 调整鉴权顺序,提高 apikey 的优先级,避免cookie抢占 apikey 的鉴权。 -7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/course/webSync) +7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/course/websync) 8. 修复 - 分享链接图片上传鉴权问题 9. 修复 - Mongo 连接池未释放问题。 10. 修复 - Dataset Intro 无法更新 diff --git a/packages/global/core/chat/type.d.ts b/packages/global/core/chat/type.d.ts index be0777b05..fbcd2981a 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -84,12 +84,14 @@ export type ChatHistoryItemType = HistoryItemType & { /* ------- response data ------------ */ export type moduleDispatchResType = { + // common moduleLogo?: string; price?: number; runningTime?: number; tokens?: number; model?: string; query?: string; + contextTotalLen?: number; // chat temperature?: number; @@ -116,6 +118,12 @@ export type moduleDispatchResType = { // plugin output pluginOutput?: Record; + + // text editor + textOutput?: string; + + // tf switch + tfSwitchResult?: boolean; }; export type ChatHistoryItemResType = moduleDispatchResType & { diff --git a/packages/global/core/module/api.d.ts b/packages/global/core/module/api.d.ts index 2af9ed7eb..55677fe90 100644 --- a/packages/global/core/module/api.d.ts +++ b/packages/global/core/module/api.d.ts @@ -1,3 +1,16 @@ import { VectorModelItemType } from '../ai/model.d'; export type SelectedDatasetType = { datasetId: string; vectorModel: VectorModelItemType }[]; + +export type HttpBodyType = { + appId: string; + chatId?: string; + variables: Record; + data: T; +}; +export type HttpQueryType = { + appId: string; + chatId?: string; + variables: Record; + [key: string]: any; +}; diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts index 00ec4ddb8..9873aeb14 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -1,19 +1,17 @@ export enum ModuleTemplateTypeEnum { userGuide = 'userGuide', systemInput = 'systemInput', + tools = 'tools', textAnswer = 'textAnswer', - dataset = 'dataset', functionCall = 'functionCall', externalCall = 'externalCall', personalPlugin = 'personalPlugin', - communityPlugin = 'communityPlugin', - commercialPlugin = 'commercialPlugin', other = 'other' } -export enum ModuleDataTypeEnum { +export enum ModuleIOValueTypeEnum { string = 'string', number = 'number', boolean = 'boolean', @@ -44,6 +42,9 @@ export enum ModuleInputKeyEnum { aiModel = 'model', aiSystemPrompt = 'systemPrompt', description = 'description', + anyInput = 'system_anyInput', + textareaInput = 'system_textareaInput', + addInputParam = 'system_addInputParam', // history historyMaxAmount = 'maxContext', @@ -69,7 +70,10 @@ export enum ModuleInputKeyEnum { extractKeys = 'extractKeys', // http - httpUrl = 'url', + httpReqUrl = 'system_httpReqUrl', + httpHeader = 'system_httpHeader', + httpMethod = 'system_httpMethod', + abandon_httpUrl = 'url', // app runAppSelectApp = 'app', @@ -87,6 +91,8 @@ export enum ModuleOutputKeyEnum { answerText = 'answerText', // answer module text key success = 'success', failed = 'failed', + text = 'system_text', + addOutputParam = 'system_addOutputParam', // dataset datasetIsEmpty = 'isEmpty', @@ -94,7 +100,11 @@ export enum ModuleOutputKeyEnum { datasetQuoteQA = 'quoteQA', // context extract - contextExtractFields = 'fields' + contextExtractFields = 'fields', + + // tf switch + resultTrue = 'system_resultTrue', + resultFalse = 'system_resultFalse' } export enum VariableInputEnum { @@ -102,3 +112,5 @@ export enum VariableInputEnum { textarea = 'textarea', select = 'select' } + +export const DYNAMIC_INPUT_KEY = 'DYNAMIC_INPUT_KEY'; diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index 4459486bc..c23ab3043 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -2,32 +2,39 @@ export enum FlowNodeInputTypeEnum { systemInput = 'systemInput', // history, userChatInput, variableInput input = 'input', // one line input - textarea = 'textarea', numberInput = 'numberInput', select = 'select', slider = 'slider', - custom = 'custom', target = 'target', // data input switch = 'switch', + textarea = 'textarea', + + addInputParam = 'addInputParam', // params input + selectApp = 'selectApp', + // chat special input aiSettings = 'aiSettings', - // model select + // ai model select selectChatModel = 'selectChatModel', selectCQModel = 'selectCQModel', + selectExtractModel = 'selectExtractModel', // dataset special input selectDataset = 'selectDataset', selectDatasetParamsModal = 'selectDatasetParamsModal', - hidden = 'hidden' + hidden = 'hidden', + custom = 'custom' } export enum FlowNodeOutputTypeEnum { answer = 'answer', source = 'source', - hidden = 'hidden' + hidden = 'hidden', + + addOutputParam = 'addOutputParam' } export enum FlowNodeTypeEnum { @@ -45,7 +52,10 @@ export enum FlowNodeTypeEnum { pluginModule = 'pluginModule', pluginInput = 'pluginInput', pluginOutput = 'pluginOutput', + textEditor = 'textEditor', // abandon variable = 'variable' } + +export const EDGE_TYPE = 'smoothstep'; diff --git a/packages/global/core/module/node/type.d.ts b/packages/global/core/module/node/type.d.ts index 27effc322..d0b47b08d 100644 --- a/packages/global/core/module/node/type.d.ts +++ b/packages/global/core/module/node/type.d.ts @@ -1,6 +1,7 @@ import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from './constant'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants'; import { SelectedDatasetType } from '../api'; +import { EditInputFieldMap, EditOutputFieldMap } from './type'; export type FlowNodeChangeProps = { moduleId: string; @@ -20,27 +21,34 @@ export type FlowNodeChangeProps = { }; export type FlowNodeInputItemType = { + valueType?: `${ModuleIOValueTypeEnum}`; // data type + type: `${FlowNodeInputTypeEnum}`; // Node Type. Decide on a render style key: `${ModuleInputKeyEnum}` | string; - type: `${FlowNodeInputTypeEnum}`; // Decide on a render style value?: any; - valueType?: `${ModuleDataTypeEnum}`; // data type label: string; description?: string; required?: boolean; + edit?: boolean; // Whether to allow editing - connected?: boolean; // unConnected field will be deleted + editField?: EditInputFieldMap; + defaultEditField?: EditNodeFieldType; + + connected?: boolean; // There are incoming data showTargetInApp?: boolean; showTargetInPlugin?: boolean; - placeholder?: string; // input,textarea - list?: { label: string; value: any }[]; // select - step?: number; // slider max?: number; - max?: number; - min?: number; - markList?: { label: string; value: any }[]; // slider + hideInApp?: boolean; + hideInPlugin?: boolean; - plusField?: boolean; // plus system will show + placeholder?: string; // input,textarea + + list?: { label: string; value: any }[]; // select + + markList?: { label: string; value: any }[]; // slider + step?: number; // slider + max?: number; // slider, number input + min?: number; // slider, number input }; export type FlowNodeOutputTargetItemType = { @@ -48,15 +56,41 @@ export type FlowNodeOutputTargetItemType = { key: string; }; export type FlowNodeOutputItemType = { - key: `${ModuleOutputKeyEnum}` | string; - label?: string; - edit?: boolean; - description?: string; - valueType?: `${ModuleDataTypeEnum}`; type?: `${FlowNodeOutputTypeEnum}`; + key: `${ModuleOutputKeyEnum}` | string; + valueType?: `${ModuleIOValueTypeEnum}`; + + label?: string; + description?: string; + + edit?: boolean; + editField?: EditOutputFieldMap; + defaultEditField?: EditNodeFieldType; + targets: FlowNodeOutputTargetItemType[]; }; +/* --------------- edit field ------------------- */ +export type EditInputFieldMap = EditOutputFieldMap & { + inputType?: boolean; + required?: boolean; +}; +export type EditOutputFieldMap = { + name?: boolean; + key?: boolean; + description?: boolean; + dataType?: boolean; +}; +export type EditNodeFieldType = { + inputType?: `${FlowNodeInputTypeEnum}`; // input type + outputType?: `${FlowNodeOutputTypeEnum}`; + required?: boolean; + key?: string; + label?: string; + description?: string; + valueType?: `${ModuleIOValueTypeEnum}`; +}; + /* ------------- item type --------------- */ /* ai chat modules props */ export type AIChatModuleProps = { diff --git a/packages/global/core/module/template/input.ts b/packages/global/core/module/template/input.ts index 7c28962e3..a3e243ecc 100644 --- a/packages/global/core/module/template/input.ts +++ b/packages/global/core/module/template/input.ts @@ -1,13 +1,13 @@ import type { FlowNodeInputItemType } from '../node/type.d'; -import { ModuleInputKeyEnum } from '../constants'; +import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '../constants'; import { FlowNodeInputTypeEnum } from '../node/constant'; -import { ModuleDataTypeEnum } from '../constants'; +import { ModuleIOValueTypeEnum } from '../constants'; -export const Input_Template_TFSwitch: FlowNodeInputItemType = { +export const Input_Template_Switch: FlowNodeInputItemType = { key: ModuleInputKeyEnum.switch, type: FlowNodeInputTypeEnum.target, label: 'core.module.input.label.switch', - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, showTargetInApp: true, showTargetInPlugin: true }; @@ -19,7 +19,7 @@ export const Input_Template_History: FlowNodeInputItemType = { required: true, min: 0, max: 30, - valueType: ModuleDataTypeEnum.chatHistory, + valueType: ModuleIOValueTypeEnum.chatHistory, value: 6, showTargetInApp: true, showTargetInPlugin: true @@ -30,7 +30,29 @@ export const Input_Template_UserChatInput: FlowNodeInputItemType = { type: FlowNodeInputTypeEnum.target, label: 'core.module.input.label.user question', required: true, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: true, showTargetInPlugin: true }; + +export const Input_Template_AddInputParam: FlowNodeInputItemType = { + key: ModuleInputKeyEnum.addInputParam, + type: FlowNodeInputTypeEnum.addInputParam, + valueType: ModuleIOValueTypeEnum.any, + label: '', + required: false, + showTargetInApp: false, + showTargetInPlugin: false +}; + +export const Input_Template_DynamicInput: FlowNodeInputItemType = { + key: DYNAMIC_INPUT_KEY, + type: FlowNodeInputTypeEnum.target, + valueType: ModuleIOValueTypeEnum.any, + label: 'core.module.inputType.dynamicTargetInput', + description: 'core.module.input.description.dynamic input', + required: false, + showTargetInApp: false, + showTargetInPlugin: true, + hideInApp: true +}; diff --git a/packages/global/core/module/template/output.ts b/packages/global/core/module/template/output.ts index 864ad3d3d..95f65a8e9 100644 --- a/packages/global/core/module/template/output.ts +++ b/packages/global/core/module/template/output.ts @@ -1,13 +1,21 @@ import type { FlowNodeOutputItemType } from '../node/type'; import { ModuleOutputKeyEnum } from '../constants'; import { FlowNodeOutputTypeEnum } from '../node/constant'; -import { ModuleDataTypeEnum } from '../constants'; +import { ModuleIOValueTypeEnum } 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, + valueType: ModuleIOValueTypeEnum.boolean, type: FlowNodeOutputTypeEnum.source, targets: [] }; + +export const Output_Template_AddOutput: FlowNodeOutputItemType = { + key: ModuleOutputKeyEnum.addOutputParam, + type: FlowNodeOutputTypeEnum.addOutputParam, + valueType: ModuleIOValueTypeEnum.any, + label: '', + targets: [] +}; diff --git a/packages/global/core/module/template/system/history.ts b/packages/global/core/module/template/system/abandon/history.ts similarity index 71% rename from packages/global/core/module/template/system/history.ts rename to packages/global/core/module/template/system/abandon/history.ts index 62aa8d161..c53b53dee 100644 --- a/packages/global/core/module/template/system/history.ts +++ b/packages/global/core/module/template/system/abandon/history.ts @@ -2,9 +2,13 @@ import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum -} from '../../node/constant'; -import { FlowModuleTemplateType } from '../../type.d'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; +} from '../../../node/constant'; +import { FlowModuleTemplateType } from '../../../type'; +import { + ModuleIOValueTypeEnum, + ModuleInputKeyEnum, + ModuleTemplateTypeEnum +} from '../../../constants'; export const HistoryModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.historyNode, @@ -12,7 +16,7 @@ export const HistoryModule: FlowModuleTemplateType = { flowType: FlowNodeTypeEnum.historyNode, avatar: '/imgs/module/history.png', name: '聊天记录(弃用)', - intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', + intro: '聊天记录,该模块已被弃用', inputs: [ { key: ModuleInputKeyEnum.historyMaxAmount, @@ -21,7 +25,7 @@ export const HistoryModule: FlowModuleTemplateType = { description: '该记录数不代表模型可接收这么多的历史记录,具体可接收多少历史记录,取决于模型的能力,通常建议不要超过20条。', value: 6, - valueType: ModuleDataTypeEnum.number, + valueType: ModuleIOValueTypeEnum.number, min: 0, max: 100, showTargetInApp: false, @@ -30,7 +34,7 @@ export const HistoryModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.history, type: FlowNodeInputTypeEnum.hidden, - valueType: ModuleDataTypeEnum.chatHistory, + valueType: ModuleIOValueTypeEnum.chatHistory, label: '聊天记录', showTargetInApp: false, showTargetInPlugin: false @@ -40,7 +44,7 @@ export const HistoryModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.history, label: '聊天记录', - valueType: ModuleDataTypeEnum.chatHistory, + valueType: ModuleIOValueTypeEnum.chatHistory, type: FlowNodeOutputTypeEnum.source, targets: [] } diff --git a/packages/global/core/module/template/system/aiChat.ts b/packages/global/core/module/template/system/aiChat.ts index aba990f0b..b667b274b 100644 --- a/packages/global/core/module/template/system/aiChat.ts +++ b/packages/global/core/module/template/system/aiChat.ts @@ -5,14 +5,14 @@ import { } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { - ModuleDataTypeEnum, + ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; import { Input_Template_History, - Input_Template_TFSwitch, + Input_Template_Switch, Input_Template_UserChatInput } from '../input'; import { chatNodeSystemPromptTip } from '../tip'; @@ -27,13 +27,13 @@ export const AiChatModule: FlowModuleTemplateType = { intro: 'AI 大模型对话', showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, type: FlowNodeInputTypeEnum.selectChatModel, label: '对话模型', required: true, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: false, showTargetInPlugin: false }, @@ -43,7 +43,7 @@ export const AiChatModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window label: '温度', value: 0, - valueType: ModuleDataTypeEnum.number, + valueType: ModuleIOValueTypeEnum.number, min: 0, max: 10, step: 1, @@ -59,7 +59,7 @@ export const AiChatModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window label: '回复上限', value: 2000, - valueType: ModuleDataTypeEnum.number, + valueType: ModuleIOValueTypeEnum.number, min: 100, max: 4000, step: 50, @@ -78,7 +78,7 @@ export const AiChatModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.hidden, label: '返回AI内容', value: true, - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, showTargetInApp: false, showTargetInPlugin: false }, @@ -86,7 +86,7 @@ export const AiChatModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.aiChatQuoteTemplate, type: FlowNodeInputTypeEnum.hidden, label: '引用内容模板', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: false, showTargetInPlugin: false }, @@ -94,7 +94,7 @@ export const AiChatModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.aiChatQuotePrompt, type: FlowNodeInputTypeEnum.hidden, label: '引用内容提示词', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: false, showTargetInPlugin: false }, @@ -102,7 +102,7 @@ export const AiChatModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.aiChatSettingModal, type: FlowNodeInputTypeEnum.aiSettings, label: '', - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, showTargetInApp: false, showTargetInPlugin: false }, @@ -112,7 +112,7 @@ export const AiChatModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.textarea, label: '系统提示词', max: 300, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, description: chatNodeSystemPromptTip, placeholder: chatNodeSystemPromptTip, showTargetInApp: true, @@ -124,7 +124,7 @@ export const AiChatModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.target, label: '引用内容', description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", - valueType: ModuleDataTypeEnum.datasetQuote, + valueType: ModuleIOValueTypeEnum.datasetQuote, showTargetInApp: true, showTargetInPlugin: true }, @@ -135,7 +135,7 @@ export const AiChatModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.history, label: '新的上下文', description: '将本次回复内容拼接上历史记录,作为新的上下文返回', - valueType: ModuleDataTypeEnum.chatHistory, + valueType: ModuleIOValueTypeEnum.chatHistory, type: FlowNodeOutputTypeEnum.source, targets: [] }, @@ -143,7 +143,7 @@ export const AiChatModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.answerText, label: 'AI回复', description: '将在 stream 回复完毕后触发', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, type: FlowNodeOutputTypeEnum.source, targets: [] }, diff --git a/packages/global/core/module/template/system/assignedAnswer.ts b/packages/global/core/module/template/system/assignedAnswer.ts index bb35a47a1..cc48fa066 100644 --- a/packages/global/core/module/template/system/assignedAnswer.ts +++ b/packages/global/core/module/template/system/assignedAnswer.ts @@ -1,7 +1,7 @@ import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; -import { Input_Template_TFSwitch } from '../input'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; +import { Input_Template_Switch } from '../input'; import { Output_Template_Finish } from '../output'; export const AssignedAnswerModule: FlowModuleTemplateType = { @@ -12,14 +12,16 @@ export const AssignedAnswerModule: FlowModuleTemplateType = { name: '指定回复', intro: '该模块可以直接回复一段指定的内容。常用于引导、提示', inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { key: ModuleInputKeyEnum.answerText, type: FlowNodeInputTypeEnum.textarea, - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, label: '回复的内容', description: - '可以使用 \\n 来实现连续换行。\n\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n\n如传入非字符串类型数据将会自动转成字符串', + '可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串', + placeholder: + '可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串', showTargetInApp: true, showTargetInPlugin: true } diff --git a/packages/global/core/module/template/system/classifyQuestion.ts b/packages/global/core/module/template/system/classifyQuestion.ts index 75ac31885..8479c6a08 100644 --- a/packages/global/core/module/template/system/classifyQuestion.ts +++ b/packages/global/core/module/template/system/classifyQuestion.ts @@ -4,10 +4,10 @@ import { FlowNodeTypeEnum } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; import { Input_Template_History, - Input_Template_TFSwitch, + Input_Template_Switch, Input_Template_UserChatInput } from '../input'; @@ -24,11 +24,11 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { 类型4: 其他问题`, showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, type: FlowNodeInputTypeEnum.selectCQModel, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, label: '分类模型', required: true, showTargetInApp: false, @@ -37,7 +37,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.aiSystemPrompt, type: FlowNodeInputTypeEnum.textarea, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, label: '背景知识', description: '你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。', @@ -51,7 +51,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.agents, type: FlowNodeInputTypeEnum.custom, - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, label: '', value: [ { diff --git a/packages/global/core/module/template/system/contextExtract.ts b/packages/global/core/module/template/system/contextExtract.ts index 25d8982a0..422d1d628 100644 --- a/packages/global/core/module/template/system/contextExtract.ts +++ b/packages/global/core/module/template/system/contextExtract.ts @@ -5,12 +5,12 @@ import { } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { - ModuleDataTypeEnum, + ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; -import { Input_Template_History, Input_Template_TFSwitch } from '../input'; +import { Input_Template_History, Input_Template_Switch } from '../input'; export const ContextExtractModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.contentExtract, @@ -21,11 +21,20 @@ export const ContextExtractModule: FlowModuleTemplateType = { intro: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等', showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, + { + key: ModuleInputKeyEnum.aiModel, + type: FlowNodeInputTypeEnum.selectExtractModel, + valueType: ModuleIOValueTypeEnum.string, + label: '提取模型', + required: true, + showTargetInApp: false, + showTargetInPlugin: false + }, { key: ModuleInputKeyEnum.description, type: FlowNodeInputTypeEnum.textarea, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, label: '提取要求描述', description: '给AI一些对应的背景知识或要求描述,引导AI更好的完成任务', required: true, @@ -40,7 +49,7 @@ export const ContextExtractModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.target, label: '需要提取的文本', required: true, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: true, showTargetInPlugin: true }, @@ -48,9 +57,9 @@ export const ContextExtractModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.extractKeys, type: FlowNodeInputTypeEnum.custom, label: '目标字段', - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段", - value: [], // {desc: string; key: string; required: boolean;}[] + value: [], // {desc: string; key: string; required: boolean; enum: string[]}[] showTargetInApp: false, showTargetInPlugin: false } @@ -59,14 +68,14 @@ export const ContextExtractModule: FlowModuleTemplateType = { { key: ModuleOutputKeyEnum.success, label: '字段完全提取', - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, type: FlowNodeOutputTypeEnum.source, targets: [] }, { key: ModuleOutputKeyEnum.failed, label: '提取字段缺失', - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, type: FlowNodeOutputTypeEnum.source, targets: [] }, @@ -74,7 +83,7 @@ export const ContextExtractModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.contextExtractFields, label: '完整提取结果', description: '一个 JSON 字符串,例如:{"name:":"YY","Time":"2023/7/2 18:00"}', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.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 index 08825ddde..823a2b4f8 100644 --- a/packages/global/core/module/template/system/datasetSearch.ts +++ b/packages/global/core/module/template/system/datasetSearch.ts @@ -5,31 +5,31 @@ import { } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { - ModuleDataTypeEnum, + ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; -import { Input_Template_TFSwitch, Input_Template_UserChatInput } from '../input'; +import { Input_Template_Switch, Input_Template_UserChatInput } from '../input'; import { Output_Template_Finish } from '../output'; import { DatasetSearchModeEnum } from '../../../dataset/constant'; export const DatasetSearchModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.datasetSearchNode, - templateType: ModuleTemplateTypeEnum.dataset, + templateType: ModuleTemplateTypeEnum.functionCall, flowType: FlowNodeTypeEnum.datasetSearchNode, avatar: '/imgs/module/db.png', name: '知识库搜索', intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。', showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { key: ModuleInputKeyEnum.datasetSelectList, type: FlowNodeInputTypeEnum.selectDataset, label: '关联的知识库', value: [], - valueType: ModuleDataTypeEnum.selectDataset, + valueType: ModuleIOValueTypeEnum.selectDataset, list: [], required: true, showTargetInApp: false, @@ -40,7 +40,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { type: FlowNodeInputTypeEnum.hidden, label: '最低相关性', value: 0.4, - valueType: ModuleDataTypeEnum.number, + valueType: ModuleIOValueTypeEnum.number, min: 0, max: 1, step: 0.01, @@ -57,7 +57,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { label: '引用上限', description: '单次搜索最大的 Tokens 数量,中文约1字=1.7Tokens,英文约1字=1Tokens', value: 1500, - valueType: ModuleDataTypeEnum.number, + valueType: ModuleIOValueTypeEnum.number, showTargetInApp: false, showTargetInPlugin: false }, @@ -65,7 +65,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.datasetSearchMode, type: FlowNodeInputTypeEnum.hidden, label: 'core.dataset.search.Mode', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, showTargetInApp: false, showTargetInPlugin: false, value: DatasetSearchModeEnum.embedding @@ -74,7 +74,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.datasetParamsModal, type: FlowNodeInputTypeEnum.selectDatasetParamsModal, label: '', - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, showTargetInApp: false, showTargetInPlugin: false }, @@ -85,14 +85,14 @@ export const DatasetSearchModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.datasetIsEmpty, label: '搜索结果为空', type: FlowNodeOutputTypeEnum.source, - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, targets: [] }, { key: ModuleOutputKeyEnum.datasetUnEmpty, label: '搜索结果不为空', type: FlowNodeOutputTypeEnum.source, - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, targets: [] }, { @@ -101,7 +101,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { description: '始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器', type: FlowNodeOutputTypeEnum.source, - valueType: ModuleDataTypeEnum.datasetQuote, + valueType: ModuleIOValueTypeEnum.datasetQuote, targets: [] }, Output_Template_Finish diff --git a/packages/global/core/module/template/system/http.ts b/packages/global/core/module/template/system/http.ts index dbaeca824..86e9b2946 100644 --- a/packages/global/core/module/template/system/http.ts +++ b/packages/global/core/module/template/system/http.ts @@ -1,8 +1,16 @@ -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'; +import { + FlowNodeInputTypeEnum, + FlowNodeOutputTypeEnum, + FlowNodeTypeEnum +} from '../../node/constant'; +import { FlowModuleTemplateType } from '../../type'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; +import { + Input_Template_AddInputParam, + Input_Template_DynamicInput, + Input_Template_Switch +} from '../input'; +import { Output_Template_AddOutput, Output_Template_Finish } from '../output'; export const HttpModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.httpRequest, @@ -13,18 +21,86 @@ export const HttpModule: FlowModuleTemplateType = { intro: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)', showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { - key: ModuleInputKeyEnum.httpUrl, - type: FlowNodeInputTypeEnum.input, - valueType: ModuleDataTypeEnum.string, - label: '请求地址', - description: '请求目标地址', - placeholder: 'https://api.ai.com/getInventory', + key: ModuleInputKeyEnum.httpMethod, + type: FlowNodeInputTypeEnum.select, + valueType: ModuleIOValueTypeEnum.string, + label: 'core.module.input.label.Http Request Method', + value: 'POST', + list: [ + { + label: 'GET', + value: 'GET' + }, + { + label: 'POST', + value: 'POST' + } + ], required: true, showTargetInApp: false, showTargetInPlugin: false + }, + { + key: ModuleInputKeyEnum.httpReqUrl, + type: FlowNodeInputTypeEnum.input, + valueType: ModuleIOValueTypeEnum.string, + label: 'core.module.input.label.Http Request Url', + description: 'core.module.input.description.Http Request Url', + placeholder: 'https://api.ai.com/getInventory', + required: false, + showTargetInApp: false, + showTargetInPlugin: false + }, + { + key: ModuleInputKeyEnum.httpHeader, + type: FlowNodeInputTypeEnum.textarea, + valueType: ModuleIOValueTypeEnum.string, + label: 'core.module.input.label.Http Request Header', + description: 'core.module.input.description.Http Request Header', + placeholder: 'core.module.input.description.Http Request Header', + required: false, + showTargetInApp: false, + showTargetInPlugin: false + }, + Input_Template_DynamicInput, + { + ...Input_Template_AddInputParam, + editField: { + key: true, + name: true, + description: true, + required: true, + dataType: true + }, + defaultEditField: { + label: '', + key: '', + description: '', + inputType: FlowNodeInputTypeEnum.target, + valueType: ModuleIOValueTypeEnum.string, + required: true + } } ], - outputs: [Output_Template_Finish] + outputs: [ + Output_Template_Finish, + { + ...Output_Template_AddOutput, + editField: { + key: true, + name: true, + description: true, + dataType: true + }, + defaultEditField: { + label: '', + key: '', + description: '', + outputType: FlowNodeOutputTypeEnum.source, + valueType: ModuleIOValueTypeEnum.string + } + } + ] }; diff --git a/packages/global/core/module/template/system/runApp.ts b/packages/global/core/module/template/system/runApp.ts index b5fd99e8a..128c2040d 100644 --- a/packages/global/core/module/template/system/runApp.ts +++ b/packages/global/core/module/template/system/runApp.ts @@ -5,14 +5,14 @@ import { } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { - ModuleDataTypeEnum, + ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; import { Input_Template_History, - Input_Template_TFSwitch, + Input_Template_Switch, Input_Template_UserChatInput } from '../input'; import { Output_Template_Finish } from '../output'; @@ -26,11 +26,11 @@ export const RunAppModule: FlowModuleTemplateType = { intro: '可以选择一个其他应用进行调用', showStatus: true, inputs: [ - Input_Template_TFSwitch, + Input_Template_Switch, { key: ModuleInputKeyEnum.runAppSelectApp, type: FlowNodeInputTypeEnum.selectApp, - valueType: ModuleDataTypeEnum.selectApp, + valueType: ModuleIOValueTypeEnum.selectApp, label: '选择一个应用', description: '选择一个其他应用进行调用', required: true, @@ -45,7 +45,7 @@ export const RunAppModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.history, label: '新的上下文', description: '将该应用回复内容拼接到历史记录中,作为新的上下文返回', - valueType: ModuleDataTypeEnum.chatHistory, + valueType: ModuleIOValueTypeEnum.chatHistory, type: FlowNodeOutputTypeEnum.source, targets: [] }, @@ -53,7 +53,7 @@ export const RunAppModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.answerText, label: 'AI回复', description: '将在应用完全结束后触发', - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, type: FlowNodeOutputTypeEnum.source, targets: [] }, diff --git a/packages/global/core/module/template/system/userGuide.ts b/packages/global/core/module/template/system/userGuide.ts index 88a47faf9..1401cbbf4 100644 --- a/packages/global/core/module/template/system/userGuide.ts +++ b/packages/global/core/module/template/system/userGuide.ts @@ -1,7 +1,7 @@ import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { userGuideTip } from '../tip'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants'; export const UserGuideModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.userGuide, @@ -14,7 +14,7 @@ export const UserGuideModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.welcomeText, type: FlowNodeInputTypeEnum.hidden, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, label: '开场白', showTargetInApp: false, showTargetInPlugin: false @@ -22,7 +22,7 @@ export const UserGuideModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.variables, type: FlowNodeInputTypeEnum.hidden, - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, label: '对话框变量', value: [], showTargetInApp: false, @@ -30,7 +30,7 @@ export const UserGuideModule: FlowModuleTemplateType = { }, { key: ModuleInputKeyEnum.questionGuide, - valueType: ModuleDataTypeEnum.boolean, + valueType: ModuleIOValueTypeEnum.boolean, type: FlowNodeInputTypeEnum.switch, label: '问题引导', showTargetInApp: false, @@ -39,7 +39,7 @@ export const UserGuideModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.tts, type: FlowNodeInputTypeEnum.hidden, - valueType: ModuleDataTypeEnum.any, + valueType: ModuleIOValueTypeEnum.any, label: '语音播报', showTargetInApp: false, showTargetInPlugin: false diff --git a/packages/global/core/module/template/system/userInput.ts b/packages/global/core/module/template/system/userInput.ts index 8c5989161..3005bf33c 100644 --- a/packages/global/core/module/template/system/userInput.ts +++ b/packages/global/core/module/template/system/userInput.ts @@ -5,7 +5,7 @@ import { } from '../../node/constant'; import { FlowModuleTemplateType } from '../../type.d'; import { - ModuleDataTypeEnum, + ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum, ModuleTemplateTypeEnum @@ -22,7 +22,7 @@ export const UserInputModule: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.userChatInput, type: FlowNodeInputTypeEnum.systemInput, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, label: '用户问题', showTargetInApp: false, showTargetInPlugin: false @@ -33,7 +33,7 @@ export const UserInputModule: FlowModuleTemplateType = { key: ModuleOutputKeyEnum.userChatInput, label: '用户问题', type: FlowNodeOutputTypeEnum.source, - valueType: ModuleDataTypeEnum.string, + valueType: ModuleIOValueTypeEnum.string, targets: [] } ] diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index 11b12eea0..8690b5c49 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -1,5 +1,5 @@ import { FlowNodeTypeEnum } from './node/constant'; -import { ModuleDataTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants'; +import { ModuleIOValueTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants'; import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; export type FlowModuleTemplateType = { @@ -72,4 +72,5 @@ export type ContextExtractAgentItemType = { desc: string; key: string; required: boolean; + enum?: string; }; diff --git a/packages/global/core/module/utils.ts b/packages/global/core/module/utils.ts index 07b91b37e..476e0e2d2 100644 --- a/packages/global/core/module/utils.ts +++ b/packages/global/core/module/utils.ts @@ -1,7 +1,8 @@ import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from './node/constant'; -import { ModuleDataTypeEnum, ModuleInputKeyEnum } from './constants'; +import { ModuleIOValueTypeEnum, ModuleInputKeyEnum } from './constants'; import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; import { AppTTSConfigType, ModuleItemType, VariableItemType } from './type'; +import { Input_Template_Switch } from './template/input'; export const getGuideModule = (modules: ModuleItemType[]) => modules.find((item) => item.flowType === FlowNodeTypeEnum.userGuide); @@ -29,42 +30,64 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => { }; }; -export function formatPluginToPreviewModule( +export const getOrInitModuleInputValue = (input: FlowNodeInputItemType) => { + if (input.value !== undefined || !input.valueType) return input.value; + + const map: Record = { + [ModuleIOValueTypeEnum.boolean]: false, + [ModuleIOValueTypeEnum.number]: 0, + [ModuleIOValueTypeEnum.string]: '' + }; + + return map[input.valueType]; +}; + +export const getModuleInputUiField = (input: FlowNodeInputItemType) => { + if (input.type === FlowNodeInputTypeEnum.input || input.type === FlowNodeInputTypeEnum.textarea) { + return { + placeholder: input.placeholder || input.description + }; + } + return {}; +}; + +export function plugin2ModuleIO( 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); + const pluginOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput); return { inputs: pluginInput ? [ - getPluginTemplatePluginIdInput(pluginId), + { + // plugin id + key: ModuleInputKeyEnum.pluginId, + type: FlowNodeInputTypeEnum.hidden, + label: 'pluginId', + value: pluginId, + valueType: ModuleIOValueTypeEnum.string, + connected: true, + showTargetInApp: false, + showTargetInPlugin: false + }, + // switch + Input_Template_Switch, ...pluginInput.inputs.map((item) => ({ ...item, + ...getModuleInputUiField(item), + value: getOrInitModuleInputValue(item), edit: false, connected: false })) ] : [], - outputs: customOutput - ? customOutput.outputs.map((item) => ({ + outputs: pluginOutput + ? pluginOutput.outputs.map((item) => ({ ...item, edit: false })) diff --git a/packages/global/core/plugin/constants.ts b/packages/global/core/plugin/constants.ts index e7dd8e5dc..35d07e46d 100644 --- a/packages/global/core/plugin/constants.ts +++ b/packages/global/core/plugin/constants.ts @@ -1,4 +1,3 @@ -import { ModuleTemplateTypeEnum } from '../module/constants'; import { ModuleItemType } from '../module/type'; export const defaultModules: ModuleItemType[] = [ @@ -28,13 +27,8 @@ export const defaultModules: ModuleItemType[] = [ } ]; -export enum PluginTypeEnum { +export enum PluginSourceEnum { 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 c17b3e59b..144bb3332 100644 --- a/packages/global/core/plugin/type.d.ts +++ b/packages/global/core/plugin/type.d.ts @@ -1,6 +1,6 @@ import { ModuleTemplateTypeEnum } from 'core/module/constants'; -import type { ModuleItemType } from '../module/type.d'; -import { PluginTypeEnum } from './constants'; +import type { FlowModuleTemplateType, ModuleItemType } from '../module/type.d'; +import { PluginSourceEnum } from './constants'; export type PluginItemSchema = { _id: string; @@ -16,11 +16,12 @@ export type PluginItemSchema = { /* plugin template */ export type PluginTemplateType = { + author?: string; id: string; - type: `${PluginTypeEnum}`; + source: `${PluginSourceEnum}`; + templateType: FlowModuleTemplateType['templateType']; name: string; avatar: string; intro: string; modules: ModuleItemType[]; - templateType?: `${ModuleTemplateTypeEnum}`; }; diff --git a/packages/plugins/package.json b/packages/plugins/package.json new file mode 100644 index 000000000..d12a877a5 --- /dev/null +++ b/packages/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "@fastgpt/plugins", + "version": "1.0.0", + "dependencies": {}, + "devDependencies": { + "@types/node": "^20.8.5" + } +} diff --git a/packages/plugins/tsconfig.json b/packages/plugins/tsconfig.json new file mode 100644 index 000000000..13c4daa3b --- /dev/null +++ b/packages/plugins/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es2015", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "baseUrl": "." + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../**/*.d.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/service/core/ai/functions/createQuestionGuide.ts b/packages/service/core/ai/functions/createQuestionGuide.ts index 69f3813f2..bf2ba6f50 100644 --- a/packages/service/core/ai/functions/createQuestionGuide.ts +++ b/packages/service/core/ai/functions/createQuestionGuide.ts @@ -10,7 +10,7 @@ export async function createQuestionGuide({ messages: ChatMessageItemType[]; model: string; }) { - const ai = getAIApi(undefined, 48000); + const ai = getAIApi(undefined, 480000); const data = await ai.chat.completions.create({ model: model, temperature: 0, diff --git a/packages/service/core/plugin/controller.ts b/packages/service/core/plugin/controller.ts index aae3f3476..c60644421 100644 --- a/packages/service/core/plugin/controller.ts +++ b/packages/service/core/plugin/controller.ts @@ -1,9 +1,10 @@ import { MongoPlugin } from './schema'; import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; -import { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils'; -import { PluginType2TemplateTypeMap, PluginTypeEnum } from '@fastgpt/global/core/plugin/constants'; +import { plugin2ModuleIO } from '@fastgpt/global/core/module/utils'; +import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d'; +import { ModuleTemplateTypeEnum } from '@fastgpt/global/core/module/constants'; /* plugin id rule: @@ -14,84 +15,71 @@ import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d'; export async function splitCombinePluginId(id: string) { const splitRes = id.split('-'); - if (splitRes.length === 1 && id.length === 24) { + if (splitRes.length === 1) { return { - type: PluginTypeEnum.personal, + source: PluginSourceEnum.personal, pluginId: id }; } - const [type, pluginId] = id.split('-') as [`${PluginTypeEnum}`, string]; - if (!type || !pluginId) return Promise.reject('pluginId not found'); + const [source, pluginId] = id.split('-') as [`${PluginSourceEnum}`, string]; + if (!source || !pluginId) return Promise.reject('pluginId not found'); - return { type, pluginId: id }; + return { source, pluginId: id }; } + +const getPluginTemplateById = async (id: string): Promise => { + const { source, pluginId } = await splitCombinePluginId(id); + if (source === PluginSourceEnum.community) { + const item = global.communityPlugins?.find((plugin) => plugin.id === pluginId); + if (!item) return Promise.reject('plugin not found'); + + return item; + } + if (source === PluginSourceEnum.personal) { + const item = await MongoPlugin.findById(id).lean(); + if (!item) return Promise.reject('plugin not found'); + return { + id: String(item._id), + name: item.name, + avatar: item.avatar, + intro: item.intro, + source: PluginSourceEnum.personal, + modules: item.modules, + templateType: ModuleTemplateTypeEnum.personalPlugin + }; + } + return Promise.reject('plugin not found'); +}; + /* 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 getPluginTemplateById(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, - templateType: PluginType2TemplateTypeMap[plugin.type], + templateType: plugin.templateType, flowType: FlowNodeTypeEnum.pluginModule, avatar: plugin.avatar, name: plugin.name, intro: plugin.intro, showStatus: true, - ...formatPluginToPreviewModule(plugin.id, plugin.modules) + ...plugin2ModuleIO(plugin.id, plugin.modules) }; } +/* run plugin time */ 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'); + const plugin = await getPluginTemplateById(id); return { id: plugin.id, - type: plugin.type, + source: plugin.source, + templateType: plugin.templateType, name: plugin.name, avatar: plugin.avatar, intro: plugin.intro, diff --git a/packages/service/support/permission/auth/plugin.ts b/packages/service/support/permission/auth/plugin.ts index 98aecc3b7..6bbedd008 100644 --- a/packages/service/support/permission/auth/plugin.ts +++ b/packages/service/support/permission/auth/plugin.ts @@ -7,7 +7,7 @@ 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'; +import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; export async function authPluginCrud({ id, @@ -66,13 +66,13 @@ export async function authPluginCanUse({ teamId: string; tmbId: string; }) { - const { type, pluginId } = await splitCombinePluginId(id); + const { source, pluginId } = await splitCombinePluginId(id); - if (type === PluginTypeEnum.community) { + if (source === PluginSourceEnum.community) { return true; } - if (type === PluginTypeEnum.personal) { + if (source === PluginSourceEnum.personal) { const { role } = await getTeamInfoByTmbId({ tmbId }); const plugin = await MongoPlugin.findOne({ _id: pluginId, teamId }); if (!plugin) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93372c046..7c3a04724 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,12 @@ importers: specifier: ^5.0.4 version: registry.npmmirror.com/@types/turndown@5.0.4 + packages/plugins: + devDependencies: + '@types/node': + specifier: ^20.8.5 + version: registry.npmmirror.com/@types/node@20.8.7 + packages/service: dependencies: '@fastgpt/global': @@ -161,6 +167,9 @@ importers: '@fastgpt/global': specifier: workspace:* version: link:../../packages/global + '@fastgpt/plugins': + specifier: workspace:* + version: link:../../packages/plugins '@fastgpt/service': specifier: workspace:* version: link:../../packages/service diff --git a/projects/app/package.json b/projects/app/package.json index 9c3840781..b57958d4b 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.6.4", + "version": "4.6.5", "private": false, "scripts": { "dev": "next dev", @@ -16,6 +16,7 @@ "@chakra-ui/system": "^2.5.8", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", + "@fastgpt/plugins": "workspace:*", "@fastgpt/global": "workspace:*", "@fastgpt/service": "workspace:*", "@fastgpt/web": "workspace:*", diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index c179a6d78..1fd0a8037 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -5,7 +5,7 @@ 3. 新增 - 分享链接更多嵌入方式提示,更多DIY方式。 4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。 5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块 -6. 链接读取支持多选择器。参考[Web 站点同步用法](https://doc.fastgpt.in/docs/course/webSync) +6. 链接读取支持多选择器。参考[Web 站点同步用法](https://doc.fastgpt.in/docs/course/websync) 7. [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/) 8. [知识库提示词详解](https://doc.fastgpt.in/docs/use-cases/ai_settings/#引用模板--引用提示词) 9. [使用文档](https://doc.fastgpt.in/docs/intro/) diff --git a/projects/app/public/imgs/module/textEditor.svg b/projects/app/public/imgs/module/textEditor.svg new file mode 100644 index 000000000..bee20bf70 --- /dev/null +++ b/projects/app/public/imgs/module/textEditor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/module/tfSwitch.svg b/projects/app/public/imgs/module/tfSwitch.svg new file mode 100644 index 000000000..b305665c4 --- /dev/null +++ b/projects/app/public/imgs/module/tfSwitch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 36881897f..fda5700ca 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -22,7 +22,6 @@ "Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations", "Chat logs": "Chat Logs", "Confirm Del App Tip": "Confirm to delete the app and all its chats", - "Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!", "Connection is invalid": "Connecting is invalid", "Connection type is different": "Connection type is different", "Copy Module Config": "Copy config", @@ -52,7 +51,7 @@ "My Modules": "My Custom Modules", "No Modules": "No module", "System Module": "System Module", - "type": "{{type}}\n{{example}}" + "type": "{{type}}\n{{description}}" }, "modules": { "Title is required": "Title is required" @@ -89,32 +88,6 @@ "share": "Share", "test": "Test Chat " }, - "response": { - "module cq": "Question classification list", - "module cq result": "Classification Result", - "module extract description": "Extract Description", - "module extract result": "Extract Result", - "module historyPreview": "Messages", - "module http body": "Body", - "module http result": "Response", - "module http url": "Request Url", - "module limit": "Count Limit", - "module maxToken": "MaxTokens", - "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", - "module time": "Running Time", - "module tokens": "Tokens", - "plugin output": "Plugin Output" - }, "retry": "Retry" }, "common": { @@ -239,6 +212,11 @@ "system": { "Help Chatbot": "Chatbot Helper", "Use Helper": "UsingHelp" + }, + "ui": { + "textarea": { + "Magnifying": "Magnifying" + } } }, "core": { @@ -262,6 +240,13 @@ "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.", "Welcome Text": "Welcome Text", "create app": "Create App", + "edit": { + "Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!", + "Out Ad Edit": "You are about to exit the Advanced orchestration page, please confirm", + "Prompt Editor": "Prompt Editor", + "Save and out": "Save out", + "UnSave": "UnSave" + }, "logs": { "Source And Time": "Source & Time" }, @@ -325,6 +310,34 @@ "Read Quote": "Read Quote", "Read Source": "Read Source" }, + "response": { + "context total length": "Context Length", + "module cq": "Question classification list", + "module cq result": "Classification Result", + "module extract description": "Extract Description", + "module extract result": "Extract Result", + "module historyPreview": "Messages", + "module http body": "Body", + "module http result": "Response", + "module http url": "Request Url", + "module limit": "Count Limit", + "module maxToken": "MaxTokens", + "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", + "module time": "Running Time", + "module tokens": "Tokens", + "plugin output": "Plugin Output", + "text output": "Text Output" + }, "tts": { "Stop Speech": "Stop" } @@ -506,14 +519,42 @@ "Plugin output must connect": "Custom outputs must all be connected", "Variable": "Variable", "Variable Setting": "Variable Setting", + "edit": { + "Field Already Exist": "Key already exist", + "Field Edit": "Field Edit" + }, + "extract": { + "Enum Description": "Lists the possible values for the field, one per row", + "Enum Value": "Enum", + "Field Description Placeholder": "Name/age /sql statement......", + "Field Setting Title": "Extract field configuration" + }, "input": { + "Add Input": "Add Input", + "Input Number": "Input: {{length}}", + "description": { + "Http Request Header": "", + "Http Request Url": "", + "TFSwitch textarea": "", + "anyInput": "", + "dynamic input": "", + "textEditor textarea": "The passed variable can be referenced by {{key}}." + }, "label": { - "chat history": "", - "switch": "", - "user question": "" + "Http Request Header": "", + "Http Request Method": "", + "Http Request Url": "", + "TFSwitch textarea": "", + "anyInput": "", + "chat history": "chat history", + "switch": "Switch", + "textEditor textarea": "Text Edit", + "user question": "User question" } }, "inputType": { + "chat history": "History", + "dynamicTargetInput": "dynamic Target Input", "input": "Input", "selectApp": "App Selector", "selectChatModel": "Select Chat Model", @@ -523,18 +564,34 @@ "textarea": "Textarea" }, "output": { + "Add Output": "Add Output", + "Output Number": "Output: {{length}}", "description": { - "running done": "running done" + "running done": "Triggered when the module call ends" }, "label": { - "running done": "running done" + "result false": "", + "result true": "", + "running done": "End of module call ", + "text": "Text output" } }, + "template": { + "TFSwitch": "", + "TFSwitch intro": "", + "UnKnow Module": "UnKnow Module", + "textEditor": "Text Editor", + "textEditor intro": "Output of fixed or incoming text after edit" + }, + "textEditor": { + "Text Edit": "Text Edit" + }, "valueType": { "any": "Any", "boolean": "Boolean", "chatHistory": "History", "datasetQuote": "Dataset Quote", + "dynamicTargetInput": "Dynamic Input", "number": "Number", "selectApp": "Select App", "selectDataset": "Select Dataset", diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 21a61a267..1f3011cc9 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -22,7 +22,6 @@ "Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录", "Chat logs": "对话日志", "Confirm Del App Tip": "确认删除该应用及其所有聊天记录?", - "Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!", "Connection is invalid": "连接无效", "Connection type is different": "连接的类型不一致", "Copy Module Config": "复制配置", @@ -52,7 +51,7 @@ "My Modules": "", "No Modules": "还没有模块~", "System Module": "系统模块", - "type": "\"{{type}}\"类型\n{{example}}" + "type": "\"{{type}}\"类型\n{{description}}" }, "modules": { "Title is required": "模块名不能为空" @@ -89,32 +88,6 @@ "share": "外部链接调用", "test": "测试" }, - "response": { - "module cq": "问题分类列表", - "module cq result": "分类结果", - "module extract description": "提取要求描述", - "module extract result": "提取结果", - "module historyPreview": "完整记录", - "module http body": "请求体", - "module http result": "响应体", - "module http url": "请求地址", - "module limit": "单次搜索上限", - "module maxToken": "最大 Tokens", - "module model": "模型", - "module name": "模型名", - "module price": "计费", - "module query": "问题/检索词", - "module question": "问题", - "module quoteList": "引用内容", - "module runningTime": "运行时长", - "module search query": "检索词", - "module search response": "搜索结果", - "module similarity": "相似度", - "module temperature": "温度", - "module time": "运行时长", - "module tokens": "Tokens", - "plugin output": "插件输出值" - }, "retry": "重新生成" }, "common": { @@ -239,6 +212,11 @@ "system": { "Help Chatbot": "机器人助手", "Use Helper": "使用帮助" + }, + "ui": { + "textarea": { + "Magnifying": "放大" + } } }, "core": { @@ -262,6 +240,13 @@ "TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。", "Welcome Text": "对话开场白", "create app": "创建属于你的 AI 应用", + "edit": { + "Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!", + "Out Ad Edit": "您即将退出高级编排页面,请确认", + "Prompt Editor": "提示词编辑", + "Save and out": "保存并退出", + "UnSave": "不保存" + }, "logs": { "Source And Time": "来源 & 时间" }, @@ -325,6 +310,34 @@ "Read Quote": "查看引用", "Read Source": "查看来源" }, + "response": { + "context total length": "上下文总长度", + "module cq": "问题分类列表", + "module cq result": "分类结果", + "module extract description": "提取要求描述", + "module extract result": "提取结果", + "module historyPreview": "完整记录", + "module http body": "请求体", + "module http result": "响应体", + "module http url": "请求地址", + "module limit": "单次搜索上限", + "module maxToken": "最大 Tokens", + "module model": "模型", + "module name": "模型名", + "module price": "计费", + "module query": "问题/检索词", + "module question": "问题", + "module quoteList": "引用内容", + "module runningTime": "运行时长", + "module search query": "检索词", + "module search response": "搜索结果", + "module similarity": "相似度", + "module temperature": "温度", + "module time": "运行时长", + "module tokens": "Tokens", + "plugin output": "插件输出值", + "text output": "文本输出" + }, "tts": { "Stop Speech": "停止" } @@ -506,14 +519,42 @@ "Plugin output must connect": "自定义输出必须全部连接", "Variable": "参数变量", "Variable Setting": "变量设置", + "edit": { + "Field Already Exist": "key 重复", + "Field Edit": "字段编辑" + }, + "extract": { + "Enum Description": "列举出该字段可能的值,每行一个", + "Enum Value": "枚举值", + "Field Description Placeholder": "姓名/年龄/sql语句……", + "Field Setting Title": "提取字段配置" + }, "input": { + "Add Input": "添加入参", + "Input Number": "入参: {{length}}", + "description": { + "Http Request Header": "自定义请求头,请严格填入JSON字符串。\n1. 确保最后一个属性没有逗号\n2. 确保 key 包含双引号\n例如: {\"Authorization\":\"Bearer xxx\"}", + "Http Request Url": "新的HTTP请求地址。如果出现两个“请求地址”,可以删除该模块重新加入,会拉取最新的模块配置。", + "TFSwitch textarea": "允许定义一些字符串来实现 false 匹配,每行一个,支持正则表达式。", + "anyInput": "可传入任意内容", + "dynamic input": "接收用户动态添加的参数,会在运行时将这些参数平铺传入", + "textEditor textarea": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。" + }, "label": { + "Http Request Header": "请求头", + "Http Request Method": "请求方式", + "Http Request Url": "请求地址", + "TFSwitch textarea": "自定义 False 匹配规则", + "anyInput": "任意内容输入", "chat history": "聊天记录", "switch": "触发器", + "textEditor textarea": "文本编辑", "user question": "用户问题" } }, "inputType": { + "chat history": "历史记录", + "dynamicTargetInput": "动态外部数据", "input": "输入框", "selectApp": "应用选择", "selectChatModel": "对话模型选择", @@ -523,18 +564,34 @@ "textarea": "段落输入" }, "output": { + "Add Output": "添加出参", + "Output Number": "出参: {{length}}", "description": { "running done": "模块调用结束时触发" }, "label": { - "running done": "模块调用结束" + "result false": "False", + "result true": "True", + "running done": "模块调用结束", + "text": "文本输出" } }, + "template": { + "TFSwitch": "判断器", + "TFSwitch intro": "根据传入的内容进行 True False 输出。默认情况下,当传入的内容为 false, undefined, null, 0, none 时,会输出 false。你也可以增加一些自定义的字符串来补充输出 false 的内容。", + "UnKnow Module": "未知模块", + "textEditor": "文本加工", + "textEditor intro": "可对固定或传入的文本进行加工后输出" + }, + "textEditor": { + "Text Edit": "文本加工" + }, "valueType": { "any": "任意", "boolean": "布尔", "chatHistory": "聊天记录", "datasetQuote": "引用内容", + "dynamicTargetInput": "动态字段输入", "number": "数字", "selectApp": "应用选择", "selectDataset": "知识库选择", diff --git a/projects/app/public/pluginTemplates/README.md b/projects/app/public/pluginTemplates/README.md index e69de29bb..cb5331895 100644 --- a/projects/app/public/pluginTemplates/README.md +++ b/projects/app/public/pluginTemplates/README.md @@ -0,0 +1,31 @@ +## 插件类型 + +xxx.json 文件 + +```ts +type TemplateType = + | 'userGuide' + | 'systemInput' + | 'tools' + | 'textAnswer' + | 'functionCall' + | 'externalCall' + | 'other'; + +type pluginType = { + author: string; // 填写作者信息 + templateType: FlowModuleTemplateType['templateType']; + name: string; + avatar: string; + intro: string; + modules: 直接从高级编排导出配置复制过来; +}; +``` + +## 额外代码怎么写? + +参考 `TFSwitch` 和 `TextEditor`,通过 HTTP 模块将数据转到一个接口中实现。提交到社区的插件,务必将所有代码都放置在 FastGPT 仓库中,可以在 `projects/app/src/pages/api/plugins` 下新建一个与**插件文件名相同**的子目录进行接口编辑。 + +## 需要装包怎么办? + +可以在 `packages/plugins` 下创建一个与**插件文件名相同**的子目录进行编写,可在 plugins 目录下安装相关依赖。然后在 FastGPT 主项目的接口中通过 `@fastgpt/plugins/xxx` 引入。 diff --git a/projects/app/public/pluginTemplates/TFSwitch.json b/projects/app/public/pluginTemplates/TFSwitch.json new file mode 100644 index 000000000..e70d784ff --- /dev/null +++ b/projects/app/public/pluginTemplates/TFSwitch.json @@ -0,0 +1,334 @@ +{ + "author": "FastGPT Team", + "templateType": "tools", + "name": "core.module.template.TFSwitch", + "avatar": "/imgs/module/tfSwitch.svg", + "intro": "core.module.template.TFSwitch intro", + "modules": [ + { + "moduleId": "w90mfp", + "name": "定义插件输入", + "avatar": "/imgs/module/input.png", + "flowType": "pluginInput", + "showStatus": false, + "position": { + "x": 616.4226348688949, + "y": -165.05298493910115 + }, + "inputs": [ + { + "key": "input", + "valueType": "any", + "type": "target", + "label": "core.module.input.label.anyInput", + "required": true, + "edit": true, + "connected": true, + "description": "core.module.input.description.anyInput" + }, + { + "key": "rule", + "valueType": "string", + "label": "core.module.input.label.TFSwitch textarea", + "type": "textarea", + "required": false, + "description": "core.module.input.description.TFSwitch textarea", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true, + "inputType": true + }, + "connected": true + } + ], + "outputs": [ + { + "key": "input", + "valueType": "any", + "label": "core.module.input.label.anyInput", + "type": "source", + "edit": true, + "targets": [ + { + "moduleId": "8kld99", + "key": "input" + } + ] + }, + { + "key": "rule", + "valueType": "string", + "label": "core.module.input.label.TFSwitch textarea", + "type": "source", + "edit": true, + "targets": [ + { + "moduleId": "8kld99", + "key": "rule" + } + ] + } + ] + }, + { + "moduleId": "tze1ju", + "name": "定义插件输出", + "avatar": "/imgs/module/output.png", + "flowType": "pluginOutput", + "showStatus": false, + "position": { + "x": 1985.3791673445353, + "y": -144.90535546692078 + }, + "inputs": [ + { + "key": "true", + "type": "target", + "valueType": "boolean", + "label": "True", + "required": true, + "edit": true, + "connected": true, + "description": "" + }, + { + "key": "false", + "valueType": "boolean", + "label": "False", + "type": "target", + "required": true, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": false, + "dataType": true, + "inputType": false + }, + "connected": true + } + ], + "outputs": [ + { + "key": "true", + "valueType": "boolean", + "label": "True", + "type": "source", + "edit": true, + "targets": [] + }, + { + "key": "false", + "valueType": "boolean", + "label": "False", + "type": "source", + "edit": true, + "targets": [] + } + ] + }, + { + "moduleId": "8kld99", + "name": "HTTP模块", + "avatar": "/imgs/module/http.png", + "flowType": "httpRequest", + "showStatus": true, + "position": { + "x": 1210.560012858087, + "y": -387.62433050951756 + }, + "inputs": [ + { + "key": "switch", + "type": "target", + "label": "core.module.input.label.switch", + "valueType": "any", + "showTargetInApp": true, + "showTargetInPlugin": true, + "connected": false + }, + { + "key": "system_httpMethod", + "type": "select", + "valueType": "string", + "label": "core.module.input.label.Http Request Method", + "value": "POST", + "list": [ + { + "label": "GET", + "value": "GET" + }, + { + "label": "POST", + "value": "POST" + } + ], + "required": true, + "showTargetInApp": false, + "showTargetInPlugin": false, + "connected": false + }, + { + "key": "system_httpReqUrl", + "type": "input", + "valueType": "string", + "label": "core.module.input.label.Http Request Url", + "description": "core.module.input.description.Http Request Url", + "placeholder": "https://api.ai.com/getInventory", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "value": "/api/plugins/TFSwitch", + "connected": false + }, + { + "key": "system_httpHeader", + "type": "textarea", + "valueType": "string", + "label": "core.module.input.label.Http Request Header", + "description": "core.module.input.description.Http Request Header", + "placeholder": "core.module.input.description.Http Request Header", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "connected": false + }, + { + "key": "input", + "valueType": "any", + "label": "input", + "type": "target", + "required": true, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true + }, + "connected": true + }, + { + "key": "rule", + "valueType": "string", + "label": "rule", + "type": "target", + "required": false, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true + }, + "connected": true + }, + { + "key": "system_addInputParam", + "type": "addInputParam", + "valueType": "any", + "label": "", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "inputType": "target", + "valueType": "string", + "required": true + }, + "connected": false + } + ], + "outputs": [ + { + "key": "finish", + "label": "core.module.output.label.running done", + "description": "core.module.output.description.running done", + "valueType": "boolean", + "type": "source", + "targets": [] + }, + { + "key": "system_addOutputParam", + "type": "addOutputParam", + "valueType": "any", + "label": "", + "targets": [], + "editField": { + "key": true, + "name": true, + "description": true, + "dataType": true + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "outputType": "source", + "valueType": "string" + } + }, + { + "type": "source", + "valueType": "boolean", + "key": "true", + "label": "true", + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "dataType": true + }, + "targets": [ + { + "moduleId": "tze1ju", + "key": "true" + } + ] + }, + { + "type": "source", + "valueType": "boolean", + "key": "false", + "label": "false", + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "dataType": true + }, + "targets": [ + { + "moduleId": "tze1ju", + "key": "false" + } + ] + } + ] + } + ] +} diff --git a/projects/app/public/pluginTemplates/TextEditor.json b/projects/app/public/pluginTemplates/TextEditor.json new file mode 100644 index 000000000..4b2cba7ac --- /dev/null +++ b/projects/app/public/pluginTemplates/TextEditor.json @@ -0,0 +1,308 @@ +{ + "author": "FastGPT Team", + "templateType": "tools", + "name": "core.module.template.textEditor", + "avatar": "/imgs/module/textEditor.svg", + "intro": "core.module.template.textEditor intro", + "modules": [ + { + "moduleId": "w90mfp", + "name": "定义插件输入", + "avatar": "/imgs/module/input.png", + "flowType": "pluginInput", + "showStatus": false, + "position": { + "x": 616.4226348688949, + "y": -165.05298493910115 + }, + "inputs": [ + { + "key": "textarea", + "valueType": "string", + "label": "core.module.input.label.textEditor textarea", + "type": "textarea", + "required": true, + "description": "core.module.input.description.textEditor textarea", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true, + "inputType": true + }, + "connected": true + }, + { + "key": "DYNAMIC_INPUT_KEY", + "valueType": "any", + "label": "字符串变量", + "type": "addInputParam", + "required": false, + "description": "可动态的添加字符串类型变量,在文本编辑中通过 {{key}} 使用变量。", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true, + "inputType": false + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "inputType": "target", + "valueType": "string", + "required": true + }, + "connected": true + } + ], + "outputs": [ + { + "key": "textarea", + "valueType": "string", + "label": "core.module.input.label.textEditor textarea", + "type": "source", + "edit": true, + "targets": [ + { + "moduleId": "49de3g", + "key": "text" + } + ] + }, + { + "key": "DYNAMIC_INPUT_KEY", + "valueType": "any", + "label": "字符串变量", + "type": "source", + "edit": true, + "targets": [ + { + "moduleId": "49de3g", + "key": "DYNAMIC_INPUT_KEY" + } + ] + } + ] + }, + { + "moduleId": "tze1ju", + "name": "定义插件输出", + "avatar": "/imgs/module/output.png", + "flowType": "pluginOutput", + "showStatus": false, + "position": { + "x": 1607.7142331269126, + "y": -145.93201540017395 + }, + "inputs": [ + { + "key": "text", + "valueType": "string", + "label": "core.module.output.label.text", + "type": "target", + "required": true, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": false, + "dataType": true, + "inputType": false + }, + "connected": true + } + ], + "outputs": [ + { + "key": "text", + "valueType": "string", + "label": "core.module.output.label.text", + "type": "source", + "edit": true, + "targets": [] + } + ] + }, + { + "moduleId": "49de3g", + "name": "HTTP模块", + "avatar": "/imgs/module/http.png", + "flowType": "httpRequest", + "showStatus": true, + "position": { + "x": 1086.8929621216014, + "y": -451.7550009773506 + }, + "inputs": [ + { + "key": "switch", + "type": "target", + "label": "core.module.input.label.switch", + "valueType": "any", + "showTargetInApp": true, + "showTargetInPlugin": true, + "connected": false + }, + { + "key": "system_httpMethod", + "type": "select", + "valueType": "string", + "label": "core.module.input.label.Http Request Method", + "value": "POST", + "list": [ + { + "label": "GET", + "value": "GET" + }, + { + "label": "POST", + "value": "POST" + } + ], + "required": true, + "showTargetInApp": false, + "showTargetInPlugin": false, + "connected": false + }, + { + "key": "system_httpReqUrl", + "type": "input", + "valueType": "string", + "label": "core.module.input.label.Http Request Url", + "description": "core.module.input.description.Http Request Url", + "placeholder": "https://api.ai.com/getInventory", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "value": "/api/plugins/textEditor", + "connected": false + }, + { + "key": "system_httpHeader", + "type": "textarea", + "valueType": "string", + "label": "core.module.input.label.Http Request Header", + "description": "core.module.input.description.Http Request Header", + "placeholder": "core.module.input.description.Http Request Header", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "value": "", + "connected": false + }, + { + "key": "DYNAMIC_INPUT_KEY", + "type": "target", + "valueType": "any", + "label": "core.module.inputType.dynamicTargetInput", + "description": "core.module.input.description.dynamic input", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": true, + "hideInApp": true, + "connected": true + }, + { + "key": "text", + "valueType": "string", + "label": "text", + "type": "target", + "required": true, + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true + }, + "connected": true + }, + { + "key": "system_addInputParam", + "type": "addInputParam", + "valueType": "any", + "label": "", + "required": false, + "showTargetInApp": false, + "showTargetInPlugin": false, + "editField": { + "key": true, + "name": true, + "description": true, + "required": true, + "dataType": true + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "inputType": "target", + "valueType": "string", + "required": true + }, + "connected": false + } + ], + "outputs": [ + { + "key": "finish", + "label": "core.module.output.label.running done", + "description": "core.module.output.description.running done", + "valueType": "boolean", + "type": "source", + "targets": [] + }, + { + "key": "system_addOutputParam", + "type": "addOutputParam", + "valueType": "any", + "label": "", + "targets": [], + "editField": { + "key": true, + "name": true, + "description": true, + "dataType": true + }, + "defaultEditField": { + "label": "", + "key": "", + "description": "", + "outputType": "source", + "valueType": "string" + } + }, + { + "type": "source", + "valueType": "string", + "key": "text", + "label": "core.module.output.label.text", + "description": "", + "edit": true, + "editField": { + "key": true, + "name": true, + "description": true, + "dataType": true + }, + "targets": [ + { + "moduleId": "tze1ju", + "key": "text" + } + ] + } + ] + } + ] +} diff --git a/projects/app/src/components/ChatBox/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 46e193467..6536b8187 100644 --- a/projects/app/src/components/ChatBox/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/WholeResponseModal.tsx @@ -21,6 +21,7 @@ function Row({ value?: string | number; rawDom?: React.ReactNode; }) { + const { t } = useTranslation(); const theme = useTheme(); const val = value || rawDom; const strValue = `${value}`; @@ -29,7 +30,7 @@ function Row({ return val !== undefined && val !== '' && val !== 'undefined' ? ( - {label}: + {t(label)}: - {item.moduleName} + {t(item.moduleName)} ), id: `${i}` })), - [response] + [response, t] ); const [currentTab, setCurrentTab] = useState(`0`); @@ -103,26 +104,33 @@ const WholeResponseModal = ({ - + {activeModule?.price !== undefined && ( )} - - - + + + + {/* ai chat */} - - + + @@ -148,7 +156,7 @@ const WholeResponseModal = ({ /> {activeModule.quoteList && activeModule.quoteList.length > 0 && ( )} @@ -161,27 +169,27 @@ const WholeResponseModal = ({ value={t(DatasetSearchModeMap[activeModule.searchMode]?.title)} /> )} - - + + {/* classify question */} { if (!activeModule?.cqList) return ''; return activeModule.cqList.map((item) => `* ${item.value}`).join('\n'); })()} /> - + {/* extract */} {activeModule?.extractResult && ( )} @@ -189,13 +197,13 @@ const WholeResponseModal = ({ {/* http */} {activeModule?.body && ( )} {activeModule?.httpResult && ( )} @@ -203,10 +211,13 @@ const WholeResponseModal = ({ {/* plugin */} {activeModule?.pluginOutput && ( )} + + {/* text editor */} + diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx index b0b210212..3e28d17a1 100644 --- a/projects/app/src/components/ChatBox/index.tsx +++ b/projects/app/src/components/ChatBox/index.tsx @@ -500,7 +500,7 @@ const ChatBox = ( return { bg: colorMap[chatContent.status] || colorMap.loading, - name: chatContent.moduleName || t('common.Loading') + name: t(chatContent.moduleName || '') || t('common.Loading') }; }, [chatHistory, isChatting, t]); /* style end */ @@ -517,7 +517,7 @@ const ChatBox = ( }; }, [router.query]); - // add guide text listener + // add listener useEffect(() => { const windowMessage = ({ data }: MessageEvent<{ type: 'sendPrompt'; text: string }>) => { if (data?.type === 'sendPrompt' && data?.text) { @@ -536,9 +536,9 @@ const ChatBox = ( }); return () => { + window.removeEventListener('message', windowMessage); eventBus.off(EventNameEnum.sendQuestion); eventBus.off(EventNameEnum.editQuestion); - window.removeEventListener('message', windowMessage); }; }, [handleSubmit, resetInputVal, sendPrompt]); diff --git a/projects/app/src/components/common/Textarea/PromptTextarea/index.tsx b/projects/app/src/components/common/Textarea/PromptTextarea/index.tsx new file mode 100644 index 000000000..8a8c61479 --- /dev/null +++ b/projects/app/src/components/common/Textarea/PromptTextarea/index.tsx @@ -0,0 +1,113 @@ +import React, { useMemo, useState } from 'react'; + +import { + Box, + Button, + ModalBody, + ModalFooter, + Textarea, + TextareaProps, + useDisclosure +} from '@chakra-ui/react'; +import MyTooltip from '@/components/MyTooltip'; +import { useTranslation } from 'next-i18next'; +import MyIcon from '@/components/Icon'; +import MyModal from '@/components/MyModal'; + +type Props = TextareaProps & { + title?: string; + showSetModalModeIcon?: boolean; + // variables: string[]; +}; + +const PromptTextarea = (props: Props) => { + const { t } = useTranslation(); + const { title = t('core.app.edit.Prompt Editor'), value, ...childProps } = props; + + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + + {isOpen && ( + + + + + + + + + )} + + ); +}; + +export default PromptTextarea; + +const Editor = React.memo(function Editor({ + showSetModalModeIcon = true, + onSetModalMode, + ...props +}: Props & { onSetModalMode?: () => void }) { + const { t } = useTranslation(); + + return ( + + + showSetModalModeIcon + value={getValues('aiSettings.systemPrompt')} + onChange={(e) => { + setValue('aiSettings.systemPrompt', e.target.value || ''); + setRefresh(!refresh); + }} + /> )} @@ -438,7 +445,7 @@ function ConfigForm({ )} - + {isOpenAIChatSetting && ( (null); + const forbidRefresh = useRef(false); + const initSign = useRef(false); + const [isEmbed, setIdEmbed] = useState(true); const { localUId, @@ -161,7 +161,7 @@ const OutLink = ({ const res = await getInitOutLinkChatInfo({ chatId, shareId, - outLinkUid: authToken || localUId + outLinkUid }); const history = res.history.map((item) => ({ ...item, @@ -176,12 +176,21 @@ const OutLink = ({ ChatBoxRef.current?.resetHistory(history); ChatBoxRef.current?.resetVariables(res.variables); - if (res.history.length > 0) { + // send init message + if (!initSign.current) { + initSign.current = true; + if (window !== top) { + window.top?.postMessage({ type: 'shareChatReady' }, '*'); + } + } + + if (chatId && res.history.length > 0) { setTimeout(() => { ChatBoxRef.current?.scrollToBottom('auto'); }, 500); } } catch (e: any) { + console.log(e); toast({ status: 'error', title: getErrText(e, t('core.shareChat.Init Error')) @@ -194,16 +203,14 @@ const OutLink = ({ } }); } - if (e?.statusText === OutLinkErrEnum.linkUnInvalid) { - router.replace('/'); - } } return null; }, - [authToken, localUId, router, setChatData, t, toast] + [outLinkUid, router, setChatData, t, toast] ); - useQuery(['init', shareId, chatId], () => { + + const { isFetching } = useQuery(['init', shareId, chatId], () => { if (forbidRefresh.current) { forbidRefresh.current = false; return null; @@ -223,11 +230,8 @@ const OutLink = ({ return null; }); - // check is embed + // window init useEffect(() => { - if (window !== top) { - window.top?.postMessage({ type: 'shareChatReady' }, '*'); - } setIdEmbed(window !== top); }, []); @@ -258,7 +262,7 @@ const OutLink = ({ {chatData.app.name} - + {showHistory === '1' ? ((children: React.ReactNode) => { return isPc ? ( @@ -372,7 +376,7 @@ const OutLink = ({ /> - + ); }; diff --git a/projects/app/src/pages/plugin/edit/Header.tsx b/projects/app/src/pages/plugin/edit/Header.tsx index d4b4301e4..f91b8fa2c 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 from 'react'; +import React, { useCallback } 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'; @@ -7,10 +7,12 @@ import { useCopyData } from '@/web/common/hooks/useCopyData'; import dynamic from 'next/dynamic'; import MyIcon from '@/components/Icon'; import MyTooltip from '@/components/MyTooltip'; -import { flowNode2Modules, useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider'; +import { useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider'; +import { flowNode2Modules } from '@/components/core/module/utils'; import { putUpdatePlugin } from '@/web/core/plugin/api'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; import { ModuleItemType } from '@fastgpt/global/core/module/type'; +import { useToast } from '@/web/common/hooks/useToast'; const ImportSettings = dynamic(() => import('@/components/core/module/Flow/ImportSettings')); const PreviewPlugin = dynamic(() => import('./Preview')); @@ -20,58 +22,83 @@ type Props = { plugin: PluginItemSchema; onClose: () => void }; const Header = ({ plugin, onClose }: Props) => { const theme = useTheme(); const { t } = useTranslation(); + const { toast } = useToast(); const { copyData } = useCopyData(); const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure(); const { nodes, edges, onFixView } = useFlowProviderStore(); const [previewModules, setPreviewModules] = React.useState(); - const { mutate: onclickSave, isLoading } = useRequest({ - mutationFn: () => { - const modules = flowNode2Modules({ nodes, edges }); + const flow2ModulesAndCheck = useCallback(() => { + const modules = flowNode2Modules({ nodes, edges }); - // check required connect - for (let i = 0; i < modules.length; i++) { - const item = modules[i]; + // check required connect + for (let i = 0; i < modules.length; i++) { + const item = modules[i]; - // update custom input connected - if (item.flowType === FlowNodeTypeEnum.pluginInput) { - item.inputs.forEach((item) => { - item.connected = true; + // update custom input connected + if (item.flowType === FlowNodeTypeEnum.pluginInput) { + item.inputs.forEach((item) => { + item.connected = true; + }); + if (item.outputs.find((output) => output.targets.length === 0)) { + toast({ + status: 'warning', + title: t('module.Plugin input must connect') }); - if (item.outputs.find((output) => output.targets.length === 0)) { - return Promise.reject(t('module.Plugin input must connect')); - } - } - 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) => { - 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}】存在未填或未连接参数`); + return false; } } - - // plugin must have input - const pluginInputModule = modules.find( - (item) => item.flowType === FlowNodeTypeEnum.pluginInput - ); - - if (!pluginInputModule) { - return Promise.reject(t('module.Plugin input is required')); - } - if (pluginInputModule.inputs.length < 1) { - return Promise.reject(t('module.Plugin input is not value')); + if ( + item.flowType === FlowNodeTypeEnum.pluginOutput && + item.inputs.find((input) => !input.connected) + ) { + toast({ + status: 'warning', + title: t('core.module.Plugin output must connect') + }); + return false; } + 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; + }) + ) { + toast({ + status: 'warning', + title: `【${item.name}】存在未填或未连接参数` + }); + return false; + } + } + + // plugin must have input + const pluginInputModule = modules.find( + (item) => item.flowType === FlowNodeTypeEnum.pluginInput + ); + + if (!pluginInputModule) { + toast({ + status: 'warning', + title: t('module.Plugin input is required') + }); + return false; + } + if (pluginInputModule.inputs.length < 1) { + toast({ + status: 'warning', + title: t('module.Plugin input is not value') + }); + return false; + } + + return modules; + }, [edges, nodes, t, toast]); + + const { mutate: onclickSave, isLoading } = useRequest({ + mutationFn: (modules: ModuleItemType[]) => { return putUpdatePlugin({ id: plugin._id, modules @@ -90,7 +117,7 @@ const Header = ({ plugin, onClose }: Props) => { alignItems={'center'} userSelect={'none'} > - + } @@ -125,12 +152,12 @@ const Header = ({ plugin, onClose }: Props) => { borderRadius={'lg'} variant={'base'} aria-label={'save'} - onClick={() => - copyData( - JSON.stringify(flowNode2Modules({ nodes, edges }), null, 2), - t('app.Export Config Successful') - ) - } + onClick={() => { + const modules = flow2ModulesAndCheck(); + if (modules) { + copyData(JSON.stringify(modules, null, 2), t('app.Export Config Successful')); + } + }} /> @@ -141,7 +168,10 @@ const Header = ({ plugin, onClose }: Props) => { aria-label={'save'} variant={'base'} onClick={() => { - setPreviewModules(flowNode2Modules({ nodes, edges })); + const modules = flow2ModulesAndCheck(); + if (modules) { + setPreviewModules(modules); + } }} /> @@ -151,7 +181,12 @@ const Header = ({ plugin, onClose }: Props) => { borderRadius={'lg'} isLoading={isLoading} aria-label={'save'} - onClick={onclickSave} + onClick={() => { + const modules = flow2ModulesAndCheck(); + if (modules) { + onclickSave(modules); + } + }} /> diff --git a/projects/app/src/pages/plugin/edit/Preview.tsx b/projects/app/src/pages/plugin/edit/Preview.tsx index 5af2eabe3..f9e43d1b6 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 { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils'; +import { plugin2ModuleIO } from '@fastgpt/global/core/module/utils'; import MyModal from '@/components/MyModal'; import { Box } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; @@ -37,7 +37,7 @@ const PreviewPlugin = ({ avatar: plugin.avatar, name: plugin.name, intro: plugin.intro, - ...formatPluginToPreviewModule(plugin._id, modules) + ...plugin2ModuleIO(plugin._id, modules) } }) ]); diff --git a/projects/app/src/pages/plugin/edit/index.tsx b/projects/app/src/pages/plugin/edit/index.tsx index 5e0acbea7..20890df28 100644 --- a/projects/app/src/pages/plugin/edit/index.tsx +++ b/projects/app/src/pages/plugin/edit/index.tsx @@ -24,10 +24,12 @@ const Render = ({ pluginId }: Props) => { const { nodes = [] } = useFlowProviderStore(); const { pluginModuleTemplates, loadPluginTemplates } = usePluginStore(); - const filterTemplates = useMemo(() => { - const copyTemplates: FlowModuleTemplateType[] = JSON.parse( - JSON.stringify(pluginSystemModuleTemplates) - ); + const moduleTemplates = useMemo(() => { + const pluginTemplates = pluginModuleTemplates.filter((item) => item.id !== pluginId); + const concatTemplates = [...pluginSystemModuleTemplates, ...pluginTemplates]; + + const copyTemplates: FlowModuleTemplateType[] = JSON.parse(JSON.stringify(concatTemplates)); + const filterType: Record = { [FlowNodeTypeEnum.userGuide]: 1, [FlowNodeTypeEnum.pluginInput]: 1, @@ -45,8 +47,13 @@ const Render = ({ pluginId }: Props) => { } }); + // filter hideInPlugin inputs + copyTemplates.forEach((template) => { + template.inputs = template.inputs.filter((input) => !input.hideInPlugin); + }); + return copyTemplates; - }, [nodes]); + }, [nodes, pluginId, pluginModuleTemplates]); const { data: pluginDetail } = useQuery( ['getOnePlugin', pluginId], @@ -61,17 +68,12 @@ const Render = ({ pluginId }: Props) => { } } ); - console.log(pluginDetail); useQuery(['getPlugTemplates'], () => loadPluginTemplates()); - const filterPlugins = useMemo(() => { - return pluginModuleTemplates.filter((item) => item.id !== pluginId); - }, [pluginId, pluginModuleTemplates]); return pluginDetail ? ( router.back()} />} /> diff --git a/projects/app/src/pages/plugin/list/component/EditModal.tsx b/projects/app/src/pages/plugin/list/component/EditModal.tsx index 68f14e72c..c696654a5 100644 --- a/projects/app/src/pages/plugin/list/component/EditModal.tsx +++ b/projects/app/src/pages/plugin/list/component/EditModal.tsx @@ -1,14 +1,5 @@ import React, { useCallback, useState } from 'react'; -import { - Box, - Flex, - Button, - ModalHeader, - ModalBody, - Input, - Textarea, - IconButton -} from '@chakra-ui/react'; +import { Box, Flex, Button, ModalBody, Input, Textarea, IconButton } from '@chakra-ui/react'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useForm } from 'react-hook-form'; import { compressImgFileAndUpload } from '@/web/common/file/controller'; @@ -25,6 +16,8 @@ import { useTranslation } from 'next-i18next'; import { useConfirm } from '@/web/common/hooks/useConfirm'; import MyIcon from '@/components/Icon'; import { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller'; +import { customAlphabet } from 'nanoid'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); export type FormType = CreateOnePluginParams & { id?: string; @@ -35,7 +28,7 @@ export const defaultForm: FormType = { intro: '', modules: [ { - moduleId: 'w90mfp', + moduleId: nanoid(), name: '定义插件输入', avatar: '/imgs/module/input.png', flowType: 'pluginInput', @@ -44,30 +37,11 @@ export const defaultForm: FormType = { x: 616.4226348688949, y: -165.05298493910115 }, - inputs: [ - { - key: 'question', - valueType: 'string', - type: 'target', - label: '用户问题', - required: true, - edit: true, - connected: false - } - ], - outputs: [ - { - key: 'question', - valueType: 'string', - label: '用户问题', - type: 'source', - edit: true, - targets: [] - } - ] + inputs: [], + outputs: [] }, { - moduleId: 'tze1ju', + moduleId: nanoid(), name: '定义插件输出', avatar: '/imgs/module/output.png', flowType: 'pluginOutput', @@ -76,27 +50,8 @@ export const defaultForm: FormType = { x: 1607.7142331269126, y: -151.8669210746189 }, - inputs: [ - { - key: 'answer', - type: 'target', - valueType: 'string', - label: '答案', - required: true, - edit: true, - connected: true - } - ], - outputs: [ - { - key: 'answer', - valueType: 'string', - label: '答案', - type: 'source', - edit: true, - targets: [] - } - ] + inputs: [], + outputs: [] } ] }; @@ -127,7 +82,7 @@ const CreateModal = ({ }); const { File, onOpen: onOpenSelectFile } = useSelectFile({ - fileType: '.jpg,.png,.svg', + fileType: 'image/*', multiple: false }); diff --git a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts index 0d26e57fe..0cf56048f 100644 --- a/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts +++ b/projects/app/src/service/moduleDispatch/agent/classifyQuestion.ts @@ -40,17 +40,19 @@ export const dispatchClassifyQuestion = async (props: Props): Promise { if (cqModel.functionCall) { return functionCall({ ...props, - histories: getHistories(history, histories), + histories: chatHistories, cqModel }); } return completions({ ...props, - histories: getHistories(history, histories), + histories: chatHistories, cqModel }); })(); @@ -65,7 +67,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise { } const extractModel = global.extractModels[0]; + const chatHistories = getHistories(history, histories); const { arg, tokens } = await (async () => { if (extractModel.functionCall) { return functionCall({ ...props, - histories: getHistories(history, histories), + histories: chatHistories, extractModel }); } return completions({ ...props, - histories: getHistories(history, histories), + histories: chatHistories, extractModel }); })(); @@ -84,7 +85,8 @@ export async function dispatchContentExtract(props: Props): Promise { query: content, tokens, extractDescription: description, - extractResult: arg + extractResult: arg, + contextTotalLen: chatHistories.length + 2 } }; } @@ -100,11 +102,11 @@ async function functionCall({ { obj: ChatRoleEnum.Human, value: `<任务描述> -${description || '根据用户要求提取适当的 JSON 字符串。'} +${description || '根据用户要求获取适当的 JSON 字符串。'} - 如果字段为空,你返回空字符串。 - 不要换行。 -- 结合历史记录和文本进行提取。 +- 结合历史记录和文本进行获取。 <文本> @@ -128,7 +130,8 @@ ${content} extractKeys.forEach((item) => { properties[item.key] = { type: 'string', - description: item.desc + description: item.desc, + ...(item.enum ? { enum: item.enum.split('\n') } : {}) }; }); @@ -192,7 +195,9 @@ async function completions({ json: extractKeys .map( (item) => - `{"key":"${item.key}", "description":"${item.required}", "required":${item.required}}}` + `{"key":"${item.key}", "description":"${item.required}", "required":${item.required}${ + item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : '' + }}` ) .join('\n'), text: `${histories.map((item) => `${item.obj}:${item.value}`).join('\n')} diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts index 641c65a7e..cc33ab28a 100644 --- a/projects/app/src/service/moduleDispatch/chat/oneapi.ts +++ b/projects/app/src/service/moduleDispatch/chat/oneapi.ts @@ -202,7 +202,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise = { + [FlowNodeTypeEnum.historyNode]: dispatchHistory, + [FlowNodeTypeEnum.questionInput]: dispatchChatInput, + [FlowNodeTypeEnum.answerNode]: dispatchAnswer, + [FlowNodeTypeEnum.chatNode]: dispatchChatCompletion, + [FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch, + [FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion, + [FlowNodeTypeEnum.contentExtract]: dispatchContentExtract, + [FlowNodeTypeEnum.httpRequest]: dispatchHttpRequest, + [FlowNodeTypeEnum.runApp]: dispatchAppRequest, + [FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin, + [FlowNodeTypeEnum.pluginInput]: dispatchPluginInput, + [FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput +}; + /* running */ export async function dispatchModules({ res, @@ -111,6 +126,7 @@ export async function dispatchModules({ Object.entries(data).map(([key, val]: any) => { updateInputValue(key, val); }); + return; } function moduleOutput( @@ -119,6 +135,7 @@ export async function dispatchModules({ ): Promise { pushStore(module, result); + // const nextRunModules: RunningModuleItemType[] = []; // Assign the output value to the next module @@ -141,18 +158,19 @@ export async function dispatchModules({ }); }); - return checkModulesCanRun(nextRunModules); - } - function checkModulesCanRun(modules: RunningModuleItemType[] = []) { + // Ensure the uniqueness of running modules const set = new Set(); - const filterModules = modules.filter((module) => { + const filterModules = nextRunModules.filter((module) => { if (set.has(module.moduleId)) return false; set.add(module.moduleId); return true; }); + return checkModulesCanRun(filterModules); + } + function checkModulesCanRun(modules: RunningModuleItemType[] = []) { return Promise.all( - filterModules.map((module) => { + modules.map((module) => { if (!module.inputs.find((item: any) => item.value === undefined)) { moduleInput(module, { [ModuleInputKeyEnum.switch]: undefined }); return moduleRun(module); @@ -192,20 +210,6 @@ export async function dispatchModules({ }; const dispatchRes: Record = await (async () => { - const callbackMap: Record = { - [FlowNodeTypeEnum.historyNode]: dispatchHistory, - [FlowNodeTypeEnum.questionInput]: dispatchChatInput, - [FlowNodeTypeEnum.answerNode]: dispatchAnswer, - [FlowNodeTypeEnum.chatNode]: dispatchChatCompletion, - [FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch, - [FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion, - [FlowNodeTypeEnum.contentExtract]: dispatchContentExtract, - [FlowNodeTypeEnum.httpRequest]: dispatchHttpRequest, - [FlowNodeTypeEnum.runApp]: dispatchAppRequest, - [FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin, - [FlowNodeTypeEnum.pluginInput]: dispatchPluginInput, - [FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput - }; if (callbackMap[module.flowType]) { return callbackMap[module.flowType](props); } @@ -232,6 +236,11 @@ export async function dispatchModules({ // start process width initInput const initModules = runningModules.filter((item) => initRunningModuleType[item.flowType]); + + // runningModules.forEach((item) => { + // console.log(item); + // }); + initModules.map((module) => moduleInput(module, { ...startParams, diff --git a/projects/app/src/service/moduleDispatch/plugin/run.ts b/projects/app/src/service/moduleDispatch/plugin/run.ts index 974481431..89a0f5b3d 100644 --- a/projects/app/src/service/moduleDispatch/plugin/run.ts +++ b/projects/app/src/service/moduleDispatch/plugin/run.ts @@ -1,7 +1,11 @@ import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { dispatchModules } from '../index'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; -import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { + DYNAMIC_INPUT_KEY, + ModuleInputKeyEnum, + ModuleOutputKeyEnum +} from '@fastgpt/global/core/module/constants'; import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; import { getPluginRuntimeById } from '@fastgpt/service/core/plugin/controller'; import { authPluginCanUse } from '@fastgpt/service/support/permission/auth/plugin'; @@ -29,13 +33,37 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise item.flowType === FlowNodeTypeEnum.pluginInput); + if (!inputModule) return Promise.reject('Plugin error, It has no set input.'); + const hasDynamicInput = inputModule.inputs.find((input) => input.key === DYNAMIC_INPUT_KEY); + + const startParams: Record = (() => { + if (!hasDynamicInput) return data; + + const params: Record = { + [DYNAMIC_INPUT_KEY]: {} + }; + + for (const key in data) { + const input = inputModule.inputs.find((input) => input.key === key); + if (input) { + params[key] = data[key]; + } else { + params[DYNAMIC_INPUT_KEY][key] = data[key]; + } + } + + return params; + })(); + const { responseData, answerText } = await dispatchModules({ ...props, modules: plugin.modules.map((module) => ({ ...module, showStatus: false })), - startParams: data + startParams }); const output = responseData.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput); @@ -46,6 +74,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise sum + (item.price || 0), 0), diff --git a/projects/app/src/service/moduleDispatch/tools/http.ts b/projects/app/src/service/moduleDispatch/tools/http.ts index 93e79eb6a..4b0c2cd19 100644 --- a/projects/app/src/service/moduleDispatch/tools/http.ts +++ b/projects/app/src/service/moduleDispatch/tools/http.ts @@ -1,9 +1,14 @@ import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d'; import type { ModuleDispatchProps } from '@/types/core/chat/type'; import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; +import axios from 'axios'; +import { flatDynamicParams } from '../utils'; export type HttpRequestProps = ModuleDispatchProps<{ - [ModuleInputKeyEnum.httpUrl]: string; + [ModuleInputKeyEnum.abandon_httpUrl]: string; + [ModuleInputKeyEnum.httpMethod]: string; + [ModuleInputKeyEnum.httpReqUrl]: string; + [ModuleInputKeyEnum.httpHeader]: string; [key: string]: any; }>; export type HttpResponse = { @@ -12,39 +17,98 @@ export type HttpResponse = { [key: string]: any; }; -export const dispatchHttpRequest = async (props: Record): Promise => { - const { +export const dispatchHttpRequest = async (props: HttpRequestProps): Promise => { + let { + appId, chatId, variables, - inputs: { url, ...body } - } = props as HttpRequestProps; + inputs: { + system_httpMethod: httpMethod, + url: abandonUrl, + system_httpReqUrl: httpReqUrl, + system_httpHeader: httpHeader, + ...body + } + } = props; - const requestBody = { - ...body, - chatId, - variables - }; + body = flatDynamicParams(body); + + const { requestMethod, requestUrl, requestHeader, requestBody, requestQuery } = await (() => { + // 2024-2-12 clear + if (abandonUrl) { + return { + requestMethod: 'POST', + requestUrl: abandonUrl, + requestHeader: httpHeader, + requestBody: { + ...body, + appId, + chatId, + variables + }, + requestQuery: {} + }; + } + if (httpReqUrl) { + return { + requestMethod: httpMethod, + requestUrl: httpReqUrl, + requestHeader: httpHeader, + requestBody: { + appId, + chatId, + variables, + data: body + }, + requestQuery: { + appId, + chatId, + ...variables, + ...body + } + }; + } + + return Promise.reject('url is empty'); + })(); + + const formatBody = transformFlatJson({ ...requestBody }); + + // parse header + const headers = await (() => { + try { + if (!requestHeader) return {}; + return JSON.parse(requestHeader); + } catch (error) { + return Promise.reject('Header 为非法 JSON 格式'); + } + })(); try { const response = await fetchData({ - url, - body: requestBody + method: requestMethod, + url: requestUrl, + headers, + body: formatBody, + query: requestQuery }); return { responseData: { price: 0, - body: requestBody, + body: formatBody, httpResult: response }, ...response }; } catch (error) { + console.log(error); + return { [ModuleOutputKeyEnum.failed]: true, responseData: { price: 0, - body: requestBody, + body: formatBody, httpResult: { error } } }; @@ -52,19 +116,97 @@ export const dispatchHttpRequest = async (props: Record): Promise; body: Record; + query: Record; }): Promise> { - const response = await fetch(url, { - method: 'POST', + const { data: response } = await axios>({ + method, + baseURL: `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000}`, + url, headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + ...headers }, - body: JSON.stringify(body) - }).then((res) => res.json()); + params: method === 'GET' ? query : {}, + data: method === 'POST' ? body : {} + }); - return response; + /* + parse the json: + { + user: { + name: 'xxx', + age: 12 + } + psw: 'xxx' + } + + result: { + 'user': { + name: 'xxx', + age: 12 + }, + 'user.name': 'xxx', + 'user.age': 12, + 'psw': 'xxx' + } + */ + const parseJson = (obj: Record, prefix = '') => { + let result: Record = {}; + for (const key in obj) { + if (typeof obj[key] === 'object') { + result[key] = obj[key]; + result = { + ...result, + ...parseJson(obj[key], `${prefix}${key}.`) + }; + } else { + result[`${prefix}${key}`] = obj[key]; + } + } + return result; + }; + + return parseJson(response); +} + +function transformFlatJson(obj: Record) { + for (let key in obj) { + if (typeof obj[key] === 'object') { + transformFlatJson(obj[key]); + } + if (key.includes('.')) { + let parts = key.split('.'); + if (parts.length <= 1) continue; + + const firstKey = parts.shift(); + + if (!firstKey) continue; + + const lastKey = parts.join('.'); + + if (obj[firstKey]) { + obj[firstKey] = { + ...obj[firstKey], + [lastKey]: obj[key] + }; + } else { + obj[firstKey] = { [lastKey]: obj[key] }; + } + + transformFlatJson(obj[firstKey]); + + delete obj[key]; + } + } + return obj; } diff --git a/projects/app/src/service/moduleDispatch/tools/runApp.ts b/projects/app/src/service/moduleDispatch/tools/runApp.ts index d8a06049a..89d409bbe 100644 --- a/projects/app/src/service/moduleDispatch/tools/runApp.ts +++ b/projects/app/src/service/moduleDispatch/tools/runApp.ts @@ -8,6 +8,7 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { textAdaptGptResponse } from '@/utils/adapt'; import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { getHistories } from '../utils'; type Props = ModuleDispatchProps<{ userChatInput: string; @@ -26,6 +27,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { user, stream, detail, + histories, inputs: { userChatInput, history = [], app } } = props; @@ -52,17 +54,19 @@ export const dispatchAppRequest = async (props: Props): Promise => { }); } + const chatHistories = getHistories(history, histories); + const { responseData, answerText } = await dispatchModules({ ...props, appId: app.id, modules: appData.modules, - histories: history, + histories: chatHistories, startParams: { userChatInput } }); - const completeMessages = history.concat([ + const completeMessages = chatHistories.concat([ { obj: ChatRoleEnum.Human, value: userChatInput diff --git a/projects/app/src/service/moduleDispatch/utils.ts b/projects/app/src/service/moduleDispatch/utils.ts index d07f53f09..f55c10b09 100644 --- a/projects/app/src/service/moduleDispatch/utils.ts +++ b/projects/app/src/service/moduleDispatch/utils.ts @@ -1,4 +1,5 @@ import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; +import { DYNAMIC_INPUT_KEY } from '@fastgpt/global/core/module/constants'; export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => { if (!history) return []; @@ -7,3 +8,13 @@ export const getHistories = (history?: ChatItemType[] | number, histories: ChatI return []; }; + +export const flatDynamicParams = (params: Record) => { + const dynamicParams = params[DYNAMIC_INPUT_KEY]; + if (!dynamicParams) return params; + return { + ...params, + ...dynamicParams, + [DYNAMIC_INPUT_KEY]: undefined + }; +}; diff --git a/projects/app/src/service/support/wallet/bill/push.ts b/projects/app/src/service/support/wallet/bill/push.ts index baf8c0281..5feb100de 100644 --- a/projects/app/src/service/support/wallet/bill/push.ts +++ b/projects/app/src/service/support/wallet/bill/push.ts @@ -6,6 +6,7 @@ import { addLog } from '@fastgpt/service/common/system/log'; import type { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api.d'; import { defaultQGModels } from '@fastgpt/global/core/ai/model'; import { POST } from '@fastgpt/service/common/api/plusRequest'; +import { PostReRankProps } from '@fastgpt/global/core/ai/api'; export function createBill(data: CreateBillProps) { if (!global.systemEnv?.pluginBaseUrl) return; @@ -247,16 +248,21 @@ export function pushWhisperBill({ export function pushReRankBill({ teamId, tmbId, - source + source, + inputs }: { teamId: string; tmbId: string; source: `${BillSourceEnum}`; + inputs: PostReRankProps['inputs']; }) { const model = global.reRankModels[0]; if (!model) return { total: 0 }; - const total = model.price * PRICE_SCALE; + const textLength = inputs.reduce((sum, item) => sum + item.text.length, 0); + const ratio = Math.ceil(textLength / 1000); + + const total = model.price * PRICE_SCALE * ratio; const name = 'wallet.bill.ReRank'; createBill({ diff --git a/projects/app/src/types/app.d.ts b/projects/app/src/types/app.d.ts index 652835500..bd7bf13da 100644 --- a/projects/app/src/types/app.d.ts +++ b/projects/app/src/types/app.d.ts @@ -1,5 +1,5 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; -import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants'; +import { ModuleIOValueTypeEnum } from '@fastgpt/global/core/module/constants'; import { XYPosition } from 'reactflow'; import { AppModuleItemTypeEnum, ModulesInputItemTypeEnum } from '../constants/app'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; diff --git a/projects/app/src/utils/adapt.ts b/projects/app/src/utils/adapt.ts index 1fff51d89..bc73bc05b 100644 --- a/projects/app/src/utils/adapt.ts +++ b/projects/app/src/utils/adapt.ts @@ -2,11 +2,11 @@ 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 '@/web/core/modules/constants/flowUi'; import { customAlphabet } from 'nanoid'; 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'; +import { EDGE_TYPE } from '@fastgpt/global/core/module/node/constant'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); export const gptMessage2ChatType = (messages: ChatMessageItemType[]): ChatItemType[] => { @@ -100,14 +100,12 @@ export const appModule2FlowEdge = ({ module.outputs.forEach((output) => output.targets.forEach((target) => { edges.push({ - style: connectionLineStyle, source: module.moduleId, target: target.moduleId, sourceHandle: output.key, targetHandle: target.key, id: nanoid(), - animated: true, - type: 'buttonedge', + type: EDGE_TYPE, data: { onDelete } }); }) diff --git a/projects/app/src/web/common/file/api.ts b/projects/app/src/web/common/file/api.ts index 89bf16531..6cbc3a19b 100644 --- a/projects/app/src/web/common/file/api.ts +++ b/projects/app/src/web/common/file/api.ts @@ -9,7 +9,7 @@ export const postUploadFiles = ( onUploadProgress: (progressEvent: AxiosProgressEvent) => void ) => POST('/common/file/upload', data, { - timeout: 0, + timeout: 480000, onUploadProgress, headers: { 'Content-Type': 'multipart/form-data; charset=utf-8' diff --git a/projects/app/src/web/common/hooks/useConfirm.tsx b/projects/app/src/web/common/hooks/useConfirm.tsx index 35f9d6038..f54b9acc5 100644 --- a/projects/app/src/web/common/hooks/useConfirm.tsx +++ b/projects/app/src/web/common/hooks/useConfirm.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDisclosure, Button, ModalBody, ModalFooter } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import MyModal from '@/components/MyModal'; @@ -7,7 +7,6 @@ export const useConfirm = (props?: { title?: string; iconSrc?: string | ''; content?: string; - bg?: string; showCancel?: boolean; type?: 'common' | 'delete'; }) => { @@ -34,7 +33,6 @@ export const useConfirm = (props?: { title = map?.title || t('Warning'), iconSrc = map?.iconSrc, content, - bg = map?.bg, showCancel = true } = props || {}; const [customContent, setCustomContent] = useState(content); @@ -57,43 +55,72 @@ export const useConfirm = (props?: { [onOpen] ), ConfirmModal: useCallback( - ({ isLoading }: { isLoading?: boolean }) => ( - - {customContent} - - {showCancel && ( + ({ + closeText = t('Cancel'), + confirmText = t('Confirm'), + isLoading, + bg, + countDown = 0 + }: { + closeText?: string; + confirmText?: string; + isLoading?: boolean; + bg?: string; + countDown?: number; + }) => { + const timer = useRef(); + const [countDownAmount, setCountDownAmount] = useState(countDown); + + useEffect(() => { + timer.current = setInterval(() => { + setCountDownAmount((val) => { + if (val <= 0) { + clearInterval(timer.current); + } + return val - 1; + }); + }, 1000); + }, []); + + return ( + + {customContent} + + {showCancel && ( + + )} + - )} - - - - - ), - [bg, customContent, iconSrc, isOpen, onClose, showCancel, t, title] + + + ); + }, + [customContent, iconSrc, isOpen, onClose, showCancel, t, title] ) }; }; diff --git a/projects/app/src/web/common/hooks/useCopyData.tsx b/projects/app/src/web/common/hooks/useCopyData.tsx index b3871a9e1..30a736d74 100644 --- a/projects/app/src/web/common/hooks/useCopyData.tsx +++ b/projects/app/src/web/common/hooks/useCopyData.tsx @@ -21,6 +21,8 @@ export const useCopyData = () => { throw new Error(''); } } catch (error) { + console.log(error); + const textarea = document.createElement('textarea'); textarea.value = data; document.body.appendChild(textarea); diff --git a/projects/app/src/web/common/tools/api.ts b/projects/app/src/web/common/tools/api.ts index a9437cfea..c64f7fa59 100644 --- a/projects/app/src/web/common/tools/api.ts +++ b/projects/app/src/web/common/tools/api.ts @@ -2,4 +2,4 @@ import { GET, POST, PUT, DELETE } from '@/web/common/api/request'; import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/api.d'; export const postFetchUrls = (data: UrlFetchParams) => - POST(`/tools/urlFetch`, data); + POST(`/common/tools/urlFetch`, data); diff --git a/projects/app/src/web/core/modules/constants/dataType.ts b/projects/app/src/web/core/modules/constants/dataType.ts index 826c26da9..9d46c62ab 100644 --- a/projects/app/src/web/core/modules/constants/dataType.ts +++ b/projects/app/src/web/core/modules/constants/dataType.ts @@ -1,60 +1,48 @@ -import type { BoxProps } from '@chakra-ui/react'; -import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants'; +import { ModuleIOValueTypeEnum } 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]: { + [ModuleIOValueTypeEnum.string]: { + handlerStyle: { + background: '#36ADEF' + }, label: 'core.module.valueType.string', - value: ModuleDataTypeEnum.string, - example: '' + value: ModuleIOValueTypeEnum.string, + description: '' }, - [ModuleDataTypeEnum.number]: { + [ModuleIOValueTypeEnum.number]: { + handlerStyle: { + background: '#FB7C3C' + }, label: 'core.module.valueType.number', - value: ModuleDataTypeEnum.number, - example: '' + value: ModuleIOValueTypeEnum.number, + description: '' }, - [ModuleDataTypeEnum.boolean]: { + [ModuleIOValueTypeEnum.boolean]: { + handlerStyle: { + background: '#E7D118' + }, label: 'core.module.valueType.boolean', - value: ModuleDataTypeEnum.boolean, - example: '' + value: ModuleIOValueTypeEnum.boolean, + description: '' }, - [ModuleDataTypeEnum.chatHistory]: { + [ModuleIOValueTypeEnum.chatHistory]: { + handlerStyle: { + background: '#00A9A6' + }, label: 'core.module.valueType.chatHistory', - value: ModuleDataTypeEnum.chatHistory, - example: `{ + value: ModuleIOValueTypeEnum.chatHistory, + description: `{ obj: System | Human | AI; value: string; }[]` }, - [ModuleDataTypeEnum.datasetQuote]: { + [ModuleIOValueTypeEnum.datasetQuote]: { + handlerStyle: { + background: '#A558C9' + }, label: 'core.module.valueType.datasetQuote', - value: ModuleDataTypeEnum.datasetQuote, - example: `{ + value: ModuleIOValueTypeEnum.datasetQuote, + description: `{ id: string; datasetId: string; collectionId: string; @@ -64,19 +52,28 @@ export const FlowValueTypeMap = { a: string }[]` }, - [ModuleDataTypeEnum.any]: { + [ModuleIOValueTypeEnum.any]: { + handlerStyle: { + background: '#9CA2A8' + }, label: 'core.module.valueType.any', - value: ModuleDataTypeEnum.any, - example: '' + value: ModuleIOValueTypeEnum.any, + description: '' }, - [ModuleDataTypeEnum.selectApp]: { + [ModuleIOValueTypeEnum.selectApp]: { + handlerStyle: { + background: '#6a6efa' + }, label: 'core.module.valueType.selectApp', - value: ModuleDataTypeEnum.selectApp, - example: '' + value: ModuleIOValueTypeEnum.selectApp, + description: '' }, - [ModuleDataTypeEnum.selectDataset]: { + [ModuleIOValueTypeEnum.selectDataset]: { + handlerStyle: { + background: '#21ba45' + }, label: 'core.module.valueType.selectDataset', - value: ModuleDataTypeEnum.selectDataset, - example: '' + value: ModuleIOValueTypeEnum.selectDataset, + description: '' } }; diff --git a/projects/app/src/web/core/modules/constants/flowUi.ts b/projects/app/src/web/core/modules/constants/flowUi.ts deleted file mode 100644 index 13347f684..000000000 --- a/projects/app/src/web/core/modules/constants/flowUi.ts +++ /dev/null @@ -1,7 +0,0 @@ -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 index 07f6b925d..f39645948 100644 --- a/projects/app/src/web/core/modules/template/system.ts +++ b/projects/app/src/web/core/modules/template/system.ts @@ -1,6 +1,6 @@ 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 { HistoryModule } from '@fastgpt/global/core/module/template/system/abandon/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'; @@ -12,6 +12,7 @@ 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 @@ -68,19 +69,19 @@ export const moduleTemplatesList: moduleTemplateListType = [ label: '系统输入', list: [] }, + { + type: ModuleTemplateTypeEnum.tools, + label: '工具', + list: [] + }, { type: ModuleTemplateTypeEnum.textAnswer, label: '文本输出', list: [] }, - { - type: ModuleTemplateTypeEnum.dataset, - label: '知识库', - list: [] - }, { type: ModuleTemplateTypeEnum.functionCall, - label: '函数调用', + label: '功能调用', list: [] }, { @@ -93,59 +94,9 @@ export const moduleTemplatesList: moduleTemplateListType = [ 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/styles/theme.ts b/projects/app/src/web/styles/theme.ts index ef738ae77..966e7fc42 100644 --- a/projects/app/src/web/styles/theme.ts +++ b/projects/app/src/web/styles/theme.ts @@ -71,7 +71,7 @@ const Button = defineStyleConfig({ color: 'myBlue.700', border: '1px solid #EFF0F1', _hover: { - bg: '#3370FF1A' + background: '#3370FF1A' } }, base: { @@ -82,13 +82,16 @@ const Button = defineStyleConfig({ transition: 'background 0.3s', _hover: { color: 'myBlue.600', - bg: 'myWhite.400', + background: 'myWhite.400', boxShadow: '0 0 5px rgba(0,0,0,0.1)' }, _active: { color: 'myBlue.700' }, - _disabled: { bg: 'myGray.100 !important', color: 'myGray.700 !important' } + _disabled: { + bg: 'myGray.100 !important', + color: 'myGray.700 !important' + } }, boxBtn: { px: 3,