From d682a8252fbe102619c8ccd8358af10c99d0fe62 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Thu, 8 Aug 2024 17:45:15 +0800 Subject: [PATCH] 4.8.9 test (#2299) * perf: read file prompt * perf: read file prompt * perf: free plan tip * feat: cron job usage * perf: app templates * perf: get llm model by name * feat: support outlink upload file * fix: upload limit --- packages/service/core/ai/model.ts | 18 +- .../core/workflow/dispatch/tools/readFiles.ts | 2 +- .../app/public/imgs/app/templates/flux.svg | 1 + .../core/ai/AISettingModal/index.tsx | 9 +- .../ChatContainer/ChatBox/Input/ChatInput.tsx | 3 +- .../core/dataset/SearchParamsTip.tsx | 8 +- .../app/src/pages/api/common/file/upload.ts | 43 +- .../detail/components/SimpleApp/EditForm.tsx | 4 +- .../Flow/nodes/NodeDatasetConcat.tsx | 4 +- .../templates/SelectDatasetParams.tsx | 4 +- projects/app/src/pages/chat/index.tsx | 2 +- projects/app/src/pages/chat/share.tsx | 2 +- projects/app/src/pages/chat/team.tsx | 2 +- .../service/support/permission/auth/chat.ts | 10 +- .../app/src/web/common/file/controller.ts | 11 + projects/app/src/web/common/system/utils.ts | 7 + projects/app/src/web/core/app/templates.ts | 1043 ++++++++++------- projects/app/src/web/core/chat/utils.ts | 5 +- 18 files changed, 726 insertions(+), 452 deletions(-) create mode 100644 projects/app/public/imgs/app/templates/flux.svg diff --git a/packages/service/core/ai/model.ts b/packages/service/core/ai/model.ts index 696815c7a..2d7ef0f98 100644 --- a/packages/service/core/ai/model.ts +++ b/packages/service/core/ai/model.ts @@ -1,20 +1,28 @@ export const getLLMModel = (model?: string) => { - return global.llmModels.find((item) => item.model === model) ?? global.llmModels[0]; + return ( + global.llmModels.find((item) => item.model === model || item.name === model) ?? + global.llmModels[0] + ); }; export const getDatasetModel = (model?: string) => { return ( - global.llmModels?.filter((item) => item.datasetProcess)?.find((item) => item.model === model) ?? - global.llmModels[0] + global.llmModels + ?.filter((item) => item.datasetProcess) + ?.find((item) => item.model === model || item.name === model) ?? global.llmModels[0] ); }; export const getVectorModel = (model?: string) => { - return global.vectorModels.find((item) => item.model === model) || global.vectorModels[0]; + return ( + global.vectorModels.find((item) => item.model === model || item.name === model) || + global.vectorModels[0] + ); }; export function getAudioSpeechModel(model?: string) { return ( - global.audioSpeechModels.find((item) => item.model === model) || global.audioSpeechModels[0] + global.audioSpeechModels.find((item) => item.model === model || item.name === model) || + global.audioSpeechModels[0] ); } diff --git a/packages/service/core/workflow/dispatch/tools/readFiles.ts b/packages/service/core/workflow/dispatch/tools/readFiles.ts index 3a2b85a72..67914cf04 100644 --- a/packages/service/core/workflow/dispatch/tools/readFiles.ts +++ b/packages/service/core/workflow/dispatch/tools/readFiles.ts @@ -95,7 +95,7 @@ export const dispatchReadFiles = async (props: Props): Promise => { }) .filter(Boolean) .slice(0, maxFiles); - console.log(parseUrlList); + const readFilesResult = await Promise.all( parseUrlList .map(async (url) => { diff --git a/projects/app/public/imgs/app/templates/flux.svg b/projects/app/public/imgs/app/templates/flux.svg new file mode 100644 index 000000000..dfa377fff --- /dev/null +++ b/projects/app/public/imgs/app/templates/flux.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/src/components/core/ai/AISettingModal/index.tsx b/projects/app/src/components/core/ai/AISettingModal/index.tsx index 20cdd4635..3487086a3 100644 --- a/projects/app/src/components/core/ai/AISettingModal/index.tsx +++ b/projects/app/src/components/core/ai/AISettingModal/index.tsx @@ -20,6 +20,7 @@ import { getDocPath } from '@/web/common/system/doc'; import AIModelSelector from '@/components/Select/AIModelSelector'; import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; +import { getWebLLMModel } from '@/web/common/system/utils'; const AIChatSettingsModal = ({ onClose, @@ -44,18 +45,18 @@ const AIChatSettingsModal = ({ const showVisionSwitch = watch(NodeInputKeyEnum.aiChatVision) !== undefined; const showMaxHistoriesSlider = watch('maxHistories') !== undefined; const useVision = watch('aiChatVision'); - const selectedModel = llmModelList.find((item) => item.model === model) || llmModelList[0]; + const selectedModel = getWebLLMModel(model); const llmSupportVision = !!selectedModel?.vision; const tokenLimit = useMemo(() => { - return llmModelList.find((item) => item.model === model)?.maxResponse || 4096; - }, [llmModelList, model]); + return selectedModel?.maxResponse || 4096; + }, [selectedModel?.maxResponse]); const onChangeModel = (e: string) => { setValue('model', e); // update max tokens - const modelData = llmModelList.find((item) => item.model === e); + const modelData = getWebLLMModel(e); if (modelData) { setValue('maxToken', modelData.maxResponse / 2); } diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Input/ChatInput.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Input/ChatInput.tsx index 671aba68a..5fa3f8a55 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/Input/ChatInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/Input/ChatInput.tsx @@ -137,6 +137,7 @@ const ChatInput = ({ const { previewUrl } = await uploadFile2DB({ file: copyFile.rawFile, bucketName: 'chat', + outLinkAuthData, metadata: { chatId }, @@ -168,7 +169,7 @@ const ChatInput = ({ { manual: false, errorToast: t('common:upload_file_error'), - refreshDeps: [fileList] + refreshDeps: [fileList, outLinkAuthData, chatId] } ); const onSelectFile = useCallback( diff --git a/projects/app/src/components/core/dataset/SearchParamsTip.tsx b/projects/app/src/components/core/dataset/SearchParamsTip.tsx index 5648a107c..389772bd8 100644 --- a/projects/app/src/components/core/dataset/SearchParamsTip.tsx +++ b/projects/app/src/components/core/dataset/SearchParamsTip.tsx @@ -7,6 +7,7 @@ import { import { useTranslation } from 'next-i18next'; import React, { useMemo } from 'react'; import MyIcon from '@fastgpt/web/components/common/Icon'; +import { getWebLLMModel } from '@/web/common/system/utils'; const SearchParamsTip = ({ searchMode, @@ -34,11 +35,8 @@ const SearchParamsTip = ({ const extensionModelName = useMemo( () => - datasetSearchUsingExtensionQuery - ? llmModelList.find((item) => item.model === queryExtensionModel)?.name ?? - llmModelList[0]?.name - : undefined, - [datasetSearchUsingExtensionQuery, llmModelList, queryExtensionModel] + datasetSearchUsingExtensionQuery ? getWebLLMModel(queryExtensionModel)?.name : undefined, + [datasetSearchUsingExtensionQuery, queryExtensionModel, llmModelList] ); return ( diff --git a/projects/app/src/pages/api/common/file/upload.ts b/projects/app/src/pages/api/common/file/upload.ts index 6c9451396..da2726184 100644 --- a/projects/app/src/pages/api/common/file/upload.ts +++ b/projects/app/src/pages/api/common/file/upload.ts @@ -1,6 +1,5 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; -import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { uploadFile } from '@fastgpt/service/common/file/gridfs/controller'; import { getUploadModel } from '@fastgpt/service/common/file/multer'; import { removeFilesByPaths } from '@fastgpt/service/common/file/utils'; @@ -10,6 +9,7 @@ import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants'; import { addLog } from '@fastgpt/service/common/system/log'; import { authFrequencyLimit } from '@/service/common/frequencyLimit/api'; import { addSeconds } from 'date-fns'; +import { authChatCert } from '@/service/support/permission/auth/chat'; const authUploadLimit = (tmbId: string) => { if (!global.feConfigs.uploadFileMaxAmount) return; @@ -21,20 +21,19 @@ const authUploadLimit = (tmbId: string) => { }; async function handler(req: NextApiRequest, res: NextApiResponse) { - const start = Date.now(); - /* Creates the multer uploader */ - const upload = getUploadModel({ - maxSize: (global.feConfigs?.uploadFileMaxSize || 500) * 1024 * 1024 - }); const filePaths: string[] = []; - try { - const { teamId, tmbId } = await authCert({ req, authToken: true }); - - await authUploadLimit(tmbId); - + const start = Date.now(); + /* Creates the multer uploader */ + const upload = getUploadModel({ + maxSize: (global.feConfigs?.uploadFileMaxSize || 500) * 1024 * 1024 + }); const { file, bucketName, metadata } = await upload.doUpload(req, res); + const { teamId, tmbId, outLinkUid } = await authChatCert({ req, authToken: true }); + + await authUploadLimit(outLinkUid || tmbId); + addLog.info(`Upload file success ${file.originalname}, cost ${Date.now() - start}ms`); if (!bucketName) { @@ -51,15 +50,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { metadata: metadata }); - return { - fileId, - previewUrl: `${ReadFileBaseUrl}?filename=${file.originalname}&token=${await createFileToken({ - bucketName, - teamId, - tmbId, - fileId - })}` - }; + jsonRes(res, { + data: { + fileId, + previewUrl: `${ReadFileBaseUrl}?filename=${file.originalname}&token=${await createFileToken( + { + bucketName, + teamId, + tmbId, + fileId + } + )}` + } + }); } catch (error) { jsonRes(res, { code: 500, diff --git a/projects/app/src/pages/app/detail/components/SimpleApp/EditForm.tsx b/projects/app/src/pages/app/detail/components/SimpleApp/EditForm.tsx index 60b4f0514..0ee1985c0 100644 --- a/projects/app/src/pages/app/detail/components/SimpleApp/EditForm.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleApp/EditForm.tsx @@ -35,6 +35,7 @@ import { AppContext } from '@/pages/app/detail/components/context'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip'; +import { getWebLLMModel } from '@/web/common/system/utils'; const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal')); @@ -121,8 +122,7 @@ const EditForm = ({ [appForm.chatConfig.variables, t] ); - const selectedModel = - llmModelList.find((item) => item.model === appForm.aiSettings.model) ?? llmModelList[0]; + const selectedModel = getWebLLMModel(appForm.aiSettings.model); const tokenLimit = useMemo(() => { return selectedModel?.quoteMaxToken || 3000; }, [selectedModel.quoteMaxToken]); diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeDatasetConcat.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeDatasetConcat.tsx index 16f5d784e..1585ea981 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeDatasetConcat.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeDatasetConcat.tsx @@ -29,6 +29,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import ValueTypeLabel from './render/ValueTypeLabel'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { isWorkflowStartOutput } from '@fastgpt/global/core/workflow/template/system/workflowStart'; +import { getWebLLMModel } from '@/web/common/system/utils'; const NodeDatasetConcat = ({ data, selected }: NodeProps) => { const { t } = useTranslation(); @@ -46,8 +47,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps) => { if (item.flowNodeType === FlowNodeTypeEnum.chatNode) { const model = item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || ''; - const quoteMaxToken = - llmModelList.find((item) => item.model === model)?.quoteMaxToken || 3000; + const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken || 3000; maxTokens = Math.max(maxTokens, quoteMaxToken); } diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDatasetParams.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDatasetParams.tsx index ebe8e144b..ec7ae319e 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDatasetParams.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDatasetParams.tsx @@ -11,6 +11,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import SearchParamsTip from '@/components/core/dataset/SearchParamsTip'; import { useContextSelector } from 'use-context-selector'; import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context'; +import { getWebLLMModel } from '@/web/common/system/utils'; const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => { const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); @@ -36,8 +37,7 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => { if (item.flowNodeType === FlowNodeTypeEnum.chatNode) { const model = item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || ''; - const quoteMaxToken = - llmModelList.find((item) => item.model === model)?.quoteMaxToken || 3000; + const quoteMaxToken = getWebLLMModel(model)?.quoteMaxToken || 3000; maxTokens = Math.max(maxTokens, quoteMaxToken); } diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index d588e8772..b391ba001 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -338,7 +338,7 @@ export async function getServerSideProps(context: any) { props: { appId: context?.query?.appId || '', chatId: context?.query?.chatId || '', - ...(await serviceSideProps(context, ['file', 'app', 'chat'])) + ...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow'])) } }; } diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index 03deab02b..cbca91f0a 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -394,7 +394,7 @@ export async function getServerSideProps(context: any) { appIntro: app?.appId?.intro ?? 'intro', shareId: shareId ?? '', authToken: authToken ?? '', - ...(await serviceSideProps(context, ['file', 'app', 'chat'])) + ...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow'])) } }; } diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx index 91255c39c..c3e023d4a 100644 --- a/projects/app/src/pages/chat/team.tsx +++ b/projects/app/src/pages/chat/team.tsx @@ -337,7 +337,7 @@ export async function getServerSideProps(context: any) { chatId: context?.query?.chatId || '', teamId: context?.query?.teamId || '', teamToken: context?.query?.teamToken || '', - ...(await serviceSideProps(context, ['file', 'app', 'chat'])) + ...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow'])) } }; } diff --git a/projects/app/src/service/support/permission/auth/chat.ts b/projects/app/src/service/support/permission/auth/chat.ts index e6202518a..d72426335 100644 --- a/projects/app/src/service/support/permission/auth/chat.ts +++ b/projects/app/src/service/support/permission/auth/chat.ts @@ -103,7 +103,15 @@ export async function authChatCrud({ 3. share page (body: shareId outLinkUid) 4. team chat page (body: teamId teamToken) */ -export async function authChatCert(props: AuthModeType) { +export async function authChatCert(props: AuthModeType): Promise<{ + teamId: string; + tmbId: string; + authType: AuthUserTypeEnum; + apikey: string; + isOwner: boolean; + canWrite: boolean; + outLinkUid?: string; +}> { const { teamId, teamToken, shareId, outLinkUid } = props.req.body as OutLinkChatAuthProps; if (shareId && outLinkUid) { diff --git a/projects/app/src/web/common/file/controller.ts b/projects/app/src/web/common/file/controller.ts index dd616255c..c0ec17d9d 100644 --- a/projects/app/src/web/common/file/controller.ts +++ b/projects/app/src/web/common/file/controller.ts @@ -3,6 +3,7 @@ import { UploadImgProps } from '@fastgpt/global/common/file/api'; import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import { preUploadImgProps } from '@fastgpt/global/common/file/api'; import { compressBase64Img, type CompressImgProps } from '@fastgpt/web/common/file/img'; +import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; /** * upload file to mongo gridfs @@ -10,11 +11,13 @@ import { compressBase64Img, type CompressImgProps } from '@fastgpt/web/common/fi export const uploadFile2DB = ({ file, bucketName, + outLinkAuthData, metadata = {}, percentListen }: { file: File; bucketName: `${BucketNameEnum}`; + outLinkAuthData?: OutLinkChatAuthProps; metadata?: Record; percentListen?: (percent: number) => void; }) => { @@ -22,6 +25,14 @@ export const uploadFile2DB = ({ form.append('metadata', JSON.stringify(metadata)); form.append('bucketName', bucketName); form.append('file', file, encodeURIComponent(file.name)); + + if (outLinkAuthData) { + for (const key in outLinkAuthData) { + // @ts-ignore + outLinkAuthData[key] && form.append(key, outLinkAuthData[key]); + } + } + return postUploadFiles(form, (e) => { if (!e.total) return; diff --git a/projects/app/src/web/common/system/utils.ts b/projects/app/src/web/common/system/utils.ts index deee1f931..3d5ea1ef8 100644 --- a/projects/app/src/web/common/system/utils.ts +++ b/projects/app/src/web/common/system/utils.ts @@ -1,3 +1,5 @@ +import { useSystemStore } from './useSystemStore'; + export const downloadFetch = async ({ url, filename }: { url: string; filename: string }) => { const a = document.createElement('a'); a.href = url; @@ -6,3 +8,8 @@ export const downloadFetch = async ({ url, filename }: { url: string; filename: a.click(); document.body.removeChild(a); }; + +export const getWebLLMModel = (model?: string) => { + const list = useSystemStore.getState().llmModelList; + return list.find((item) => item.model === model || item.name === model) ?? list[0]; +}; diff --git a/projects/app/src/web/core/app/templates.ts b/projects/app/src/web/core/app/templates.ts index 2a4df5796..01037dc97 100644 --- a/projects/app/src/web/core/app/templates.ts +++ b/projects/app/src/web/core/app/templates.ts @@ -1011,6 +1011,110 @@ export const simpleBotTemplates: TemplateType = [ ]; export const workflowTemplates: TemplateType = [ + { + id: 'workflow-default', + avatar: 'core/app/type/workflowFill', + name: '空白工作流', + intro: '从零开始构建工作流', + type: AppTypeEnum.workflow, + modules: [ + { + nodeId: 'userGuide', + name: '系统配置', + intro: '可以配置应用的系统参数', + avatar: 'core/workflow/template/systemConfig', + flowNodeType: 'userGuide', + position: { + x: 262.2732338817093, + y: -476.00241136598146 + }, + version: '481', + inputs: [ + { + key: 'welcomeText', + renderTypeList: ['hidden'], + valueType: 'string', + label: 'core.app.Welcome Text', + value: '' + }, + { + key: 'variables', + renderTypeList: ['hidden'], + valueType: 'any', + label: 'core.app.Chat Variable', + value: [] + }, + { + key: 'questionGuide', + valueType: 'boolean', + renderTypeList: ['hidden'], + label: 'core.app.Question Guide', + value: false + }, + { + key: 'tts', + renderTypeList: ['hidden'], + valueType: 'any', + label: '', + value: { + type: 'web' + } + }, + { + key: 'whisper', + renderTypeList: ['hidden'], + valueType: 'any', + label: '', + value: { + open: false, + autoSend: false, + autoTTSResponse: false + } + }, + { + key: 'scheduleTrigger', + renderTypeList: ['hidden'], + valueType: 'any', + label: '', + value: null + } + ], + outputs: [] + }, + { + nodeId: '448745', + name: '流程开始', + intro: '', + avatar: 'core/workflow/template/workflowStart', + flowNodeType: 'workflowStart', + position: { + x: 632.368838596004, + y: -347.7446492944009 + }, + version: '481', + inputs: [ + { + key: 'userChatInput', + renderTypeList: ['reference', 'textarea'], + valueType: 'string', + label: '用户问题', + required: true, + toolDescription: '用户问题' + } + ], + outputs: [ + { + id: 'userChatInput', + key: 'userChatInput', + label: 'common:core.module.input.label.user question', + type: 'static', + valueType: 'string' + } + ] + } + ], + edges: [] + }, { id: 'TranslateRobot', avatar: '/imgs/app/templates/translate.svg', @@ -2022,409 +2126,6 @@ export const workflowTemplates: TemplateType = [ } ] }, - { - id: 'dalle', - avatar: '/imgs/app/templates/dalle.svg', - name: 'Dalle3绘图', - intro: '通过请求Dalle3接口绘图,需要有 api key', - type: AppTypeEnum.workflow, - modules: [ - { - nodeId: 'userGuide', - name: '系统配置', - intro: '可以配置应用的系统参数', - avatar: 'core/workflow/template/systemConfig', - flowNodeType: 'userGuide', - position: { - x: 531.2422736065552, - y: -486.7611729549753 - }, - version: '481', - inputs: [ - { - key: 'welcomeText', - renderTypeList: ['hidden'], - valueType: 'string', - label: 'core.app.Welcome Text', - value: '' - }, - { - key: 'variables', - renderTypeList: ['hidden'], - valueType: 'any', - label: 'core.app.Chat Variable', - value: [] - }, - { - key: 'questionGuide', - valueType: 'boolean', - renderTypeList: ['hidden'], - label: 'core.app.Question Guide', - value: false - }, - { - key: 'tts', - renderTypeList: ['hidden'], - valueType: 'any', - label: '', - value: { - type: 'web' - } - }, - { - key: 'whisper', - renderTypeList: ['hidden'], - valueType: 'any', - label: '', - value: { - open: false, - autoSend: false, - autoTTSResponse: false - } - }, - { - key: 'scheduleTrigger', - renderTypeList: ['hidden'], - valueType: 'any', - label: '', - value: null - } - ], - outputs: [] - }, - { - nodeId: '448745', - name: '流程开始', - intro: '', - avatar: 'core/workflow/template/workflowStart', - flowNodeType: 'workflowStart', - position: { - x: 532.1275542407774, - y: 46.03775600322817 - }, - version: '481', - inputs: [ - { - key: 'userChatInput', - renderTypeList: ['reference', 'textarea'], - valueType: 'string', - label: '用户问题', - required: true, - toolDescription: '用户问题' - } - ], - outputs: [ - { - id: 'userChatInput', - key: 'userChatInput', - label: 'core.module.input.label.user question', - type: 'static', - valueType: 'string' - } - ] - }, - { - nodeId: 'tMyUnRL5jIrC', - name: 'HTTP 请求', - intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)', - avatar: 'core/workflow/template/httpRequest', - flowNodeType: 'httpRequest468', - showStatus: true, - position: { - x: 931.6784209157559, - y: -162.36850541742047 - }, - version: '486', - inputs: [ - { - key: 'system_addInputParam', - renderTypeList: ['addInputParam'], - valueType: 'dynamic', - label: '', - required: false, - description: 'core.module.input.description.HTTP Dynamic Input', - editField: { - key: true, - valueType: true - }, - customInputConfig: { - selectValueTypeList: [ - 'string', - 'number', - 'boolean', - 'object', - 'arrayString', - 'arrayNumber', - 'arrayBoolean', - 'arrayObject', - 'any', - 'chatHistory', - 'datasetQuote', - 'dynamic', - 'selectApp', - 'selectDataset' - ], - showDescription: false, - showDefaultValue: true - } - }, - { - key: 'system_httpMethod', - renderTypeList: ['custom'], - valueType: 'string', - label: '', - value: 'POST', - required: true - }, - { - key: 'system_httpReqUrl', - renderTypeList: ['hidden'], - valueType: 'string', - label: '', - description: 'core.module.input.description.Http Request Url', - placeholder: 'https://api.ai.com/getInventory', - required: false, - value: 'https://api.openai.com/v1/images/generations' - }, - { - key: 'system_httpHeader', - renderTypeList: ['custom'], - valueType: 'any', - value: [ - { - key: 'Authorization', - type: 'string', - value: 'Bearer ' - } - ], - label: '', - description: 'core.module.input.description.Http Request Header', - placeholder: 'core.module.input.description.Http Request Header', - required: false - }, - { - key: 'system_httpParams', - renderTypeList: ['hidden'], - valueType: 'any', - value: [], - label: '', - required: false - }, - { - key: 'system_httpJsonBody', - renderTypeList: ['hidden'], - valueType: 'any', - value: - '{\n "model": "dall-e-3",\n "prompt": "{{prompt}}",\n "n": 1,\n "size": "1024x1024"\n}', - label: '', - required: false - }, - { - key: 'prompt', - valueType: 'string', - label: 'prompt', - renderTypeList: ['reference'], - description: '', - canEdit: true, - editField: { - key: true, - valueType: true - }, - value: ['448745', 'userChatInput'] - } - ], - outputs: [ - { - id: 'system_addOutputParam', - key: 'system_addOutputParam', - type: 'dynamic', - valueType: 'dynamic', - label: '', - customFieldConfig: { - selectValueTypeList: [ - 'string', - 'number', - 'boolean', - 'object', - 'arrayString', - 'arrayNumber', - 'arrayBoolean', - 'arrayObject', - 'any', - 'chatHistory', - 'datasetQuote', - 'dynamic', - 'selectApp', - 'selectDataset' - ], - showDescription: false, - showDefaultValue: true - } - }, - { - id: 'error', - key: 'error', - label: '请求错误', - description: 'HTTP请求错误信息,成功时返回空', - valueType: 'object', - type: 'static' - }, - { - id: 'httpRawResponse', - key: 'httpRawResponse', - label: '原始响应', - required: true, - description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。', - valueType: 'any', - type: 'static' - }, - { - id: 'DeKGGioBwaMf', - type: 'dynamic', - key: 'data[0].url', - valueType: 'string', - label: 'data[0].url' - } - ] - }, - { - nodeId: '7mapnCgHfKW6', - name: '指定回复', - intro: - '该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。', - avatar: 'core/workflow/template/reply', - flowNodeType: 'answerNode', - position: { - x: 2204.4609372615846, - y: 163.11883652393863 - }, - version: '481', - inputs: [ - { - key: 'text', - renderTypeList: ['textarea', 'reference'], - valueType: 'any', - label: 'core.module.input.label.Response content', - description: 'core.module.input.description.Response content', - placeholder: 'core.module.input.description.Response content', - selectedTypeIndex: 1, - value: ['vEXJF8pQ8eOv', 'system_text'], - required: true - } - ], - outputs: [] - }, - { - nodeId: 'vEXJF8pQ8eOv', - name: '文本拼接', - intro: '可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。', - avatar: 'core/workflow/template/textConcat', - flowNodeType: 'textEditor', - position: { - x: 1544.8821308368042, - y: -27.22950739442814 - }, - version: '486', - inputs: [ - { - key: 'system_addInputParam', - renderTypeList: ['addInputParam'], - valueType: 'dynamic', - label: '', - required: false, - description: '可以引用其他节点的输出,作为文本拼接的变量,通过 {{字段名}} 来引用变量', - customInputConfig: { - selectValueTypeList: [ - 'string', - 'number', - 'boolean', - 'object', - 'arrayString', - 'arrayNumber', - 'arrayBoolean', - 'arrayObject', - 'any', - 'chatHistory', - 'datasetQuote', - 'dynamic', - 'selectApp', - 'selectDataset' - ], - showDescription: false, - showDefaultValue: false - } - }, - { - key: 'system_textareaInput', - renderTypeList: ['textarea'], - valueType: 'string', - required: true, - label: '拼接文本', - placeholder: '可通过 {{字段名}} 来引用变量', - value: '![]({{url}})' - }, - { - renderTypeList: ['reference'], - valueType: 'string', - canEdit: true, - key: 'url', - label: 'url', - customInputConfig: { - selectValueTypeList: [ - 'string', - 'number', - 'boolean', - 'object', - 'arrayString', - 'arrayNumber', - 'arrayBoolean', - 'arrayObject', - 'any', - 'chatHistory', - 'datasetQuote', - 'dynamic', - 'selectApp', - 'selectDataset' - ], - showDescription: false, - showDefaultValue: false - }, - required: true, - value: ['tMyUnRL5jIrC', 'DeKGGioBwaMf'] - } - ], - outputs: [ - { - id: 'system_text', - key: 'system_text', - label: '拼接结果', - type: 'static', - valueType: 'string' - } - ] - } - ], - edges: [ - { - source: '448745', - target: 'tMyUnRL5jIrC', - sourceHandle: '448745-source-right', - targetHandle: 'tMyUnRL5jIrC-target-left' - }, - { - source: 'tMyUnRL5jIrC', - target: 'vEXJF8pQ8eOv', - sourceHandle: 'tMyUnRL5jIrC-source-right', - targetHandle: 'vEXJF8pQ8eOv-target-left' - }, - { - source: 'vEXJF8pQ8eOv', - target: '7mapnCgHfKW6', - sourceHandle: 'vEXJF8pQ8eOv-source-right', - targetHandle: '7mapnCgHfKW6-target-left' - } - ] - }, { id: 'CQ', avatar: 'core/workflow/template/questionClassify', @@ -3151,6 +2852,542 @@ export const pluginTemplates: TemplateType = [ targetHandle: 'pluginOutput-target-left' } ] + }, + { + id: 'dalle', + avatar: '/imgs/model/openai.svg', + name: 'Dalle3绘图', + intro: '通过请求Dalle3接口绘图,需要有 api key', + type: AppTypeEnum.plugin, + modules: [ + { + nodeId: 'pluginInput', + name: '自定义插件输入', + intro: '可以配置插件需要哪些输入,利用这些输入来运行插件', + avatar: 'core/workflow/template/workflowStart', + flowNodeType: 'pluginInput', + showStatus: false, + position: { + x: 412.7756423516722, + y: -99.80686112290361 + }, + version: '481', + inputs: [ + { + renderTypeList: ['reference'], + selectedTypeIndex: 0, + valueType: 'string', + canEdit: true, + key: '绘图提示词', + label: '绘图提示词', + description: '绘图提示词', + required: true, + toolDescription: '绘图提示词' + } + ], + outputs: [ + { + id: '绘图提示词', + valueType: 'string', + key: '绘图提示词', + label: '绘图提示词', + type: 'hidden' + } + ] + }, + { + nodeId: 'pluginOutput', + name: '自定义插件输出', + intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出', + avatar: 'core/workflow/template/pluginOutput', + flowNodeType: 'pluginOutput', + showStatus: false, + position: { + x: 1822.7195641525896, + y: -193.54601587659562 + }, + version: '481', + inputs: [ + { + renderTypeList: ['reference'], + valueType: 'string', + canEdit: true, + key: '图片访问链接', + label: '图片访问链接', + description: '', + value: ['tMvel910bnrJ', 'pJXgWoTpPoMy'] + }, + { + renderTypeList: ['reference'], + valueType: 'object', + canEdit: true, + key: 'error', + label: '错误信息', + description: '', + value: ['tMvel910bnrJ', 'error'] + } + ], + outputs: [] + }, + { + nodeId: 'tMvel910bnrJ', + name: 'HTTP 请求', + intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)', + avatar: 'core/workflow/template/httpRequest', + flowNodeType: 'httpRequest468', + showStatus: true, + position: { + x: 1044.8838211811253, + y: -414.7785530936485 + }, + version: '481', + inputs: [ + { + key: 'system_addInputParam', + renderTypeList: ['addInputParam'], + valueType: 'dynamic', + label: '', + required: false, + description: 'core.module.input.description.HTTP Dynamic Input', + customInputConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: true + } + }, + { + key: 'system_httpMethod', + renderTypeList: ['custom'], + valueType: 'string', + label: '', + value: 'POST', + required: true + }, + { + key: 'system_httpReqUrl', + renderTypeList: ['hidden'], + valueType: 'string', + label: '', + description: 'core.module.input.description.Http Request Url', + placeholder: 'https://api.ai.com/getInventory', + required: false, + value: '{{url}}/v1/images/generations' + }, + { + key: 'system_httpHeader', + renderTypeList: ['custom'], + valueType: 'any', + value: [ + { + key: 'Authorization', + type: 'string', + value: 'Bearer {{authorization}}' + } + ], + label: '', + description: 'core.module.input.description.Http Request Header', + placeholder: 'core.module.input.description.Http Request Header', + required: false + }, + { + key: 'system_httpParams', + renderTypeList: ['hidden'], + valueType: 'any', + value: [], + label: '', + required: false + }, + { + key: 'system_httpJsonBody', + renderTypeList: ['hidden'], + valueType: 'any', + value: + '{\n "model": "dall-e-3",\n "prompt": "{{prompt}}",\n "n": 1,\n "size": "1024x1024"\n}', + label: '', + required: false + }, + { + renderTypeList: ['reference'], + valueType: 'string', + canEdit: true, + key: 'prompt', + label: 'prompt', + customInputConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: true + }, + required: true, + value: ['pluginInput', '绘图提示词'] + } + ], + outputs: [ + { + id: 'error', + key: 'error', + label: '请求错误', + description: 'HTTP请求错误信息,成功时返回空', + valueType: 'object', + type: 'static' + }, + { + id: 'httpRawResponse', + key: 'httpRawResponse', + label: '原始响应', + required: true, + description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。', + valueType: 'any', + type: 'static' + }, + { + id: 'system_addOutputParam', + key: 'system_addOutputParam', + type: 'dynamic', + valueType: 'dynamic', + label: '', + customFieldConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: false + } + }, + { + id: 'pJXgWoTpPoMy', + valueType: 'string', + type: 'dynamic', + key: 'data[0].url', + label: 'data[0].url' + } + ] + } + ], + edges: [ + { + source: 'pluginInput', + target: 'tMvel910bnrJ', + sourceHandle: 'pluginInput-source-right', + targetHandle: 'tMvel910bnrJ-target-left' + }, + { + source: 'tMvel910bnrJ', + target: 'pluginOutput', + sourceHandle: 'tMvel910bnrJ-source-right', + targetHandle: 'pluginOutput-target-left' + } + ] + }, + { + id: 'flux', + avatar: '/imgs/app/templates/flux.svg', + name: 'Flux 绘图', + intro: '通过请求 Flux 接口绘图,需要有 api key', + type: AppTypeEnum.plugin, + modules: [ + { + nodeId: 'pluginInput', + name: '自定义插件输入', + intro: '可以配置插件需要哪些输入,利用这些输入来运行插件', + avatar: 'core/workflow/template/workflowStart', + flowNodeType: 'pluginInput', + showStatus: false, + position: { + x: 351.2046235980429, + y: -77.41739975794749 + }, + version: '481', + inputs: [ + { + renderTypeList: ['reference'], + selectedTypeIndex: 0, + valueType: 'string', + canEdit: true, + key: '绘图提示词', + label: '绘图提示词', + description: '绘图提示词', + required: true, + toolDescription: '绘图提示词' + } + ], + outputs: [ + { + id: '绘图提示词', + valueType: 'string', + key: '绘图提示词', + label: '绘图提示词', + type: 'hidden' + } + ] + }, + { + nodeId: 'pluginOutput', + name: '自定义插件输出', + intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出', + avatar: 'core/workflow/template/pluginOutput', + flowNodeType: 'pluginOutput', + showStatus: false, + position: { + x: 1983.6911708285384, + y: -95.86447885674228 + }, + version: '481', + inputs: [ + { + renderTypeList: ['reference'], + valueType: 'string', + canEdit: true, + key: '图片访问链接', + label: '图片访问链接', + description: '', + value: ['tMvel910bnrJ', 'pJXgWoTpPoMy'] + }, + { + renderTypeList: ['reference'], + valueType: 'object', + canEdit: true, + key: 'error', + label: '错误信息', + description: '', + value: ['tMvel910bnrJ', 'error'] + } + ], + outputs: [] + }, + { + nodeId: 'tMvel910bnrJ', + name: 'HTTP 请求', + intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)', + avatar: 'core/workflow/template/httpRequest', + flowNodeType: 'httpRequest468', + showStatus: true, + position: { + x: 1138.1732435351091, + y: -416.6443415407282 + }, + version: '481', + inputs: [ + { + key: 'system_addInputParam', + renderTypeList: ['addInputParam'], + valueType: 'dynamic', + label: '', + required: false, + description: 'core.module.input.description.HTTP Dynamic Input', + customInputConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: true + } + }, + { + key: 'system_httpMethod', + renderTypeList: ['custom'], + valueType: 'string', + label: '', + value: 'POST', + required: true + }, + { + key: 'system_httpReqUrl', + renderTypeList: ['hidden'], + valueType: 'string', + label: '', + description: 'core.module.input.description.Http Request Url', + placeholder: 'https://api.ai.com/getInventory', + required: false, + value: 'https://fal.run/fal-ai/flux-pro' + }, + { + key: 'system_httpHeader', + renderTypeList: ['custom'], + valueType: 'any', + value: [ + { + key: 'Authorization', + type: 'string', + value: 'Key {{apikey}}' + } + ], + label: '', + description: 'core.module.input.description.Http Request Header', + placeholder: 'core.module.input.description.Http Request Header', + required: false + }, + { + key: 'system_httpParams', + renderTypeList: ['hidden'], + valueType: 'any', + value: [], + label: '', + required: false + }, + { + key: 'system_httpJsonBody', + renderTypeList: ['hidden'], + valueType: 'any', + value: + '{\n "prompt": "{{prompt}}",\n "image_size": "landscape_4_3",\n "num_inference_steps": 28,\n "guidance_scale": 3.5\n}', + label: '', + required: false + }, + { + renderTypeList: ['reference'], + valueType: 'string', + canEdit: true, + key: 'prompt', + label: 'prompt', + customInputConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: true + }, + required: true, + value: ['pluginInput', '绘图提示词'] + } + ], + outputs: [ + { + id: 'error', + key: 'error', + label: '请求错误', + description: 'HTTP请求错误信息,成功时返回空', + valueType: 'object', + type: 'static' + }, + { + id: 'httpRawResponse', + key: 'httpRawResponse', + label: '原始响应', + required: true, + description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。', + valueType: 'any', + type: 'static' + }, + { + id: 'system_addOutputParam', + key: 'system_addOutputParam', + type: 'dynamic', + valueType: 'dynamic', + label: '', + customFieldConfig: { + selectValueTypeList: [ + 'string', + 'number', + 'boolean', + 'object', + 'arrayString', + 'arrayNumber', + 'arrayBoolean', + 'arrayObject', + 'any', + 'chatHistory', + 'datasetQuote', + 'dynamic', + 'selectApp', + 'selectDataset' + ], + showDescription: false, + showDefaultValue: false + } + }, + { + id: 'pJXgWoTpPoMy', + valueType: 'string', + type: 'dynamic', + key: 'images[0].url', + label: 'images[0].url' + } + ] + } + ], + edges: [ + { + source: 'pluginInput', + target: 'tMvel910bnrJ', + sourceHandle: 'pluginInput-source-right', + targetHandle: 'tMvel910bnrJ-target-left' + }, + { + source: 'tMvel910bnrJ', + target: 'pluginOutput', + sourceHandle: 'tMvel910bnrJ-source-right', + targetHandle: 'pluginOutput-target-left' + } + ] } ]; diff --git a/projects/app/src/web/core/chat/utils.ts b/projects/app/src/web/core/chat/utils.ts index a7ee8ddd3..e33c38ed5 100644 --- a/projects/app/src/web/core/chat/utils.ts +++ b/projects/app/src/web/core/chat/utils.ts @@ -2,12 +2,11 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; +import { getWebLLMModel } from '@/web/common/system/utils'; export function checkChatSupportSelectFileByChatModels(models: string[] = []) { - const llmModelList = useSystemStore.getState().llmModelList; - for (const model of models) { - const modelData = llmModelList.find((item) => item.model === model || item.name === model); + const modelData = getWebLLMModel(model); if (modelData?.vision) { return true; }