From b520988c64f111605d6fd2a877ce0847f36b5175 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Fri, 27 Dec 2024 20:05:12 +0800 Subject: [PATCH] V4.8.17 feature (#3485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add third party account config (#3443) * temp * editor workflow variable style * add team to dispatch * i18n * delete console * change openai account position * fix * fix * fix * fix * fix * 4.8.17 test (#3461) * perf: external provider config * perf: ui * feat: add template config (#3434) * change template position * template config * delete console * delete * fix * fix * perf: Mongo visutal field (#3464) * remve invalid code * perf: team member visutal code * perf: virtual search; perf: search test data * fix: ts * fix: image response headers * perf: template code * perf: auth layout;perf: auto save (#3472) * perf: auth layout * perf: auto save * perf: auto save * fix: template guide display & http input support external variables (#3475) * fix: template guide display * http editor support external workflow variables * perf: auto save;fix: ifelse checker line break; (#3478) * perf: auto save * perf: auto save * fix: ifelse checker line break * perf: doc * perf: doc * fix: update var type error * 4.8.17 test (#3479) * perf: auto save * perf: auto save * perf: template code * 4.8.17 test (#3480) * perf: auto save * perf: auto save * perf: model price model * feat: add react memo * perf: model provider filter * fix: ts (#3481) * perf: auto save * perf: auto save * fix: ts * simple app tool select (#3473) * workflow plugin userguide & simple tool ui * simple tool filter * reuse component * change component to hook * fix * perf: too selector modal (#3484) * perf: auto save * perf: auto save * perf: markdown render * perf: too selector * fix: app version require tmbId * perf: templates refresh * perf: templates refresh * hide auto save error tip * perf: toolkit guide --------- Co-authored-by: heheer --- README.md | 6 +- .../zh-cn/docs/development/openapi/dataset.md | 9 +- .../zh-cn/docs/development/upgrading/4817.md | 28 +- .../global/common/system/types/index.d.ts | 9 + packages/global/core/app/type.d.ts | 26 + .../global/core/dataset/collection/utils.ts | 6 +- packages/global/core/dataset/type.d.ts | 7 +- .../global/core/workflow/runtime/type.d.ts | 9 +- .../global/core/workflow/runtime/utils.ts | 3 + .../workflow/template/system/workflowStart.ts | 1 - packages/global/core/workflow/type/node.d.ts | 2 + packages/global/support/outLink/type.d.ts | 5 - packages/global/support/permission/type.d.ts | 9 +- .../global/support/user/team/controller.d.ts | 7 +- packages/global/support/user/team/type.d.ts | 32 +- packages/global/support/user/type.d.ts | 5 - packages/plugins/src/searchXNG/template.json | 3 +- packages/service/core/ai/config.ts | 11 +- .../service/core/app/plugin/controller.ts | 1 + .../core/app/templates/templateSchema.ts | 51 ++ .../core/app/templates/templateTypeSchema.ts | 25 + packages/service/core/app/version/schema.ts | 18 +- packages/service/core/chat/utils.ts | 5 +- .../core/dataset/collection/controller.ts | 34 +- .../service/core/dataset/collection/schema.ts | 7 + .../service/core/dataset/collection/utils.ts | 4 +- packages/service/core/dataset/controller.ts | 8 +- packages/service/core/dataset/data/schema.ts | 39 +- .../service/core/dataset/search/controller.ts | 15 +- .../core/dataset/training/controller.ts | 2 +- .../dispatch/agent/classifyQuestion.ts | 10 +- .../core/workflow/dispatch/agent/extract.ts | 18 +- .../dispatch/agent/runTool/functionCall.ts | 4 +- .../workflow/dispatch/agent/runTool/index.ts | 6 +- .../dispatch/agent/runTool/promptCall.ts | 4 +- .../dispatch/agent/runTool/toolChoice.ts | 6 +- .../core/workflow/dispatch/chat/oneapi.ts | 10 +- .../service/core/workflow/dispatch/index.ts | 21 +- .../core/workflow/dispatch/loop/runLoop.ts | 5 +- .../core/workflow/dispatch/tools/runIfElse.ts | 10 +- .../workflow/dispatch/tools/runUpdateVar.ts | 22 +- .../service/core/workflow/dispatch/utils.ts | 29 +- packages/service/support/outLink/schema.ts | 7 + .../service/support/permission/auth/team.ts | 28 +- .../service/support/permission/controller.ts | 37 +- .../support/permission/dataset/auth.ts | 7 +- .../permission/memberGroup/controllers.ts | 33 +- .../memberGroup/groupMemberSchema.ts | 7 + packages/service/support/permission/schema.ts | 13 + packages/service/support/user/controller.ts | 1 - .../service/support/user/team/controller.ts | 102 +++- .../support/user/team/teamMemberSchema.ts | 14 +- .../service/support/user/team/teamSchema.ts | 10 + packages/service/type.d.ts | 2 - packages/templates/README.md | 3 + packages/templates/package.json | 10 + packages/templates/register.ts | 83 +++ .../templates/src}/CQ/template.json | 0 .../templates/src}/Chinese/template.json | 6 +- .../src}/TranslateRobot/template.json | 2 +- .../templates/src}/animalLife/template.json | 2 +- .../templates/src}/chatGuide/template.json | 0 .../templates/src}/divination/template.json | 2 +- .../templates/src}/flux/template.json | 2 +- .../templates/src}/githubIssue/template.json | 6 +- .../templates/src}/google/template.json | 2 +- .../src}/longTranslate/template.json | 8 +- .../templates/src}/plugin-dalle/template.json | 2 +- .../src}/plugin-feishu/template.json | 2 +- .../src}/simpleDatasetChat/template.json | 0 .../src}/srt-translate/template.json | 6 +- .../templates/src}/stock/template.json | 2 +- .../templates/src}/timeBot/template.json | 0 packages/templates/tsconfig.json | 21 + packages/templates/type.d.ts | 5 + .../web/components/common/Avatar/index.tsx | 2 +- .../web/components/common/DndDrag/index.tsx | 2 +- .../web/components/common/Icon/constants.ts | 17 + .../common/Icon/icons/common/arrowRight.svg | 3 + .../common/Icon/icons/common/layer.svg | 3 + .../Icon/icons/common/templateMarket.svg | 5 + .../common/Icon/icons/common/thirdParty.svg | 6 + .../common/Icon/icons/common/variable.svg | 6 + .../core/app/templates/TranslateRobot.svg | 0 .../icons/core/app/templates/animalLife.svg | 0 .../Icon/icons/core/app/templates/chinese.svg | 0 .../icons/core/app/templates/divination.svg | 0 .../Icon/icons/core/app/templates/flux.svg | 0 .../icons/core/app/templates/githubIssue.svg | 0 .../Icon/icons/core/app/templates/google.svg | 0 .../icons/core/app/templates/plugin-dalle.svg | 0 .../core/app/templates/plugin-feishu.svg | 0 .../Icon/icons/core/app/templates/stock.svg | 0 .../common/Icon/icons/support/account/laf.svg | 9 + .../common/Input/NumberInput/index.tsx | 2 +- .../common/Input/SearchInput/index.tsx | 2 +- .../web/components/common/MyBox/index.tsx | 2 +- .../web/components/common/MyLoading/index.tsx | 2 +- .../common/MySlider/InputSlider.tsx | 2 +- .../common/MyTooltip/QuestionTip.tsx | 2 +- .../components/VariableLabel.tsx | 19 +- .../VariablePlugin/components/Variable.tsx | 28 + .../plugins/VariablePlugin/index.tsx | 8 +- .../plugins/VariablePlugin/node.ts | 49 -- .../plugins/VariablePlugin/node.tsx | 105 ++++ .../common/Textarea/PromptEditor/utils.ts | 7 +- packages/web/core/workflow/constants.ts | 30 + packages/web/i18n/en/account.json | 1 + packages/web/i18n/en/account_info.json | 2 - packages/web/i18n/en/account_thirdParty.json | 16 + packages/web/i18n/en/app.json | 7 +- packages/web/i18n/en/common.json | 6 + packages/web/i18n/zh-CN/account.json | 1 + packages/web/i18n/zh-CN/account_info.json | 4 - .../web/i18n/zh-CN/account_thirdParty.json | 20 + packages/web/i18n/zh-CN/app.json | 7 +- packages/web/i18n/zh-CN/common.json | 10 + packages/web/i18n/zh-Hant/account.json | 1 + packages/web/i18n/zh-Hant/account_info.json | 2 - .../web/i18n/zh-Hant/account_thirdParty.json | 16 + packages/web/i18n/zh-Hant/app.json | 7 +- packages/web/i18n/zh-Hant/common.json | 6 + packages/web/styles/theme.ts | 2 +- packages/web/types/i18next.d.ts | 3 + pnpm-lock.yaml | 21 +- projects/app/.env.template | 6 +- projects/app/package.json | 3 +- projects/app/src/components/Layout/auth.tsx | 2 +- projects/app/src/components/Layout/index.tsx | 24 +- projects/app/src/components/Layout/navbar.tsx | 1 + .../app/src/components/Markdown/index.tsx | 19 +- .../src/components/Select/AIModelSelector.tsx | 53 +- .../components/common/Modal/UseGuideModal.tsx | 46 ++ .../core/ai/AISettingModal/index.tsx | 17 +- .../components/core/ai/ModelTable/index.tsx | 64 +- .../core/app/DatasetParamsModal.tsx | 8 +- .../components/renderPluginInput.tsx | 19 +- .../core/dataset/SearchParamsTip.tsx | 4 +- .../support/laf/LafAccountModal.tsx | 4 +- .../wallet/StandardPlanContentList.tsx | 23 +- projects/app/src/global/core/app/api.d.ts | 1 + projects/app/src/instrumentation.ts | 6 +- .../account/components/AccountContainer.tsx | 7 + .../pages/account/components/TeamSelector.tsx | 2 +- projects/app/src/pages/account/info/index.tsx | 148 +---- projects/app/src/pages/account/team/index.tsx | 2 +- .../components/OpenAIAccountModal.tsx | 46 +- .../components/WorkflowVariableModal.tsx | 81 +++ .../src/pages/account/thirdParty/index.tsx | 263 ++++++++ projects/app/src/pages/api/admin/initv4817.ts | 39 ++ projects/app/src/pages/api/admin/initv485.ts | 2 + .../app/src/pages/api/common/file/read.ts | 1 + .../pages/api/common/file/read/[filename].ts | 1 + projects/app/src/pages/api/core/app/create.ts | 10 +- .../app/plugin/getSystemPluginTemplates.ts | 3 +- .../src/pages/api/core/app/template/detail.ts | 10 +- .../src/pages/api/core/app/template/list.ts | 49 +- .../src/pages/api/core/app/version/publish.ts | 18 +- .../app/src/pages/api/core/chat/chatTest.ts | 9 +- .../src/pages/api/core/chat/outLink/init.ts | 8 +- .../collection/create/reTrainingCollection.ts | 10 +- .../api/core/dataset/collection/delete.ts | 2 +- .../api/core/dataset/collection/detail.ts | 2 +- .../pages/api/core/dataset/collection/read.ts | 13 +- .../api/core/dataset/collection/update.ts | 2 +- .../pages/api/core/dataset/data/insertData.ts | 2 +- .../src/pages/api/core/dataset/data/list.ts | 2 +- .../pages/api/core/dataset/data/pushData.ts | 11 +- .../src/pages/api/core/dataset/data/update.ts | 2 +- .../pages/api/core/dataset/data/v2/list.ts | 2 +- .../app/src/pages/api/core/workflow/debug.ts | 7 +- .../api/support/user/account/tokenLogin.ts | 29 +- .../pages/api/support/user/account/update.ts | 26 +- .../user/team/thirtdParty/checkUsage.ts | 50 ++ .../app/src/pages/api/v1/chat/completions.ts | 41 +- .../detail/components/SimpleApp/EditForm.tsx | 91 +-- .../detail/components/SimpleApp/Header.tsx | 9 +- .../SimpleApp/components/ConfigToolModal.tsx | 130 ++++ .../SimpleApp/components/ToolSelect.tsx | 157 +++++ .../SimpleApp/components/ToolSelectModal.tsx | 572 +++++++++++------- .../Flow/nodes/NodeHttp/index.tsx | 95 ++- .../WorkflowComponents/Flow/nodes/NodeLaf.tsx | 6 +- .../nodes/NodePluginIO/InputEditModal.tsx | 13 + .../nodes/NodePluginIO/InputTypeConfig.tsx | 6 - .../Flow/nodes/NodeVariableUpdate.tsx | 44 +- .../Flow/nodes/render/NodeCard.tsx | 35 +- .../render/RenderInput/templates/Textarea.tsx | 27 +- .../context/workflowStatusContext.tsx | 37 +- .../pages/app/detail/components/context.tsx | 22 +- .../pages/app/list/components/CreateModal.tsx | 37 +- .../list/components/TemplateMarketModal.tsx | 181 +++--- projects/app/src/pages/chat/share.tsx | 17 +- .../detail/components/InputDataModal.tsx | 4 +- .../pages/toolkit/components/PluginCard.tsx | 67 +- .../app/src/service/common/system/index.ts | 30 +- .../service/common/system/volumnMongoWatch.ts | 18 + projects/app/src/service/core/app/template.ts | 48 -- projects/app/src/service/core/app/utils.ts | 7 +- .../support/permission/auth/outLink.ts | 7 +- .../service/support/permission/auth/team.ts | 17 - projects/app/src/types/user.d.ts | 3 - projects/app/src/web/common/api/request.ts | 2 +- projects/app/src/web/core/app/api.ts | 10 +- projects/app/src/web/core/app/api/template.ts | 22 +- projects/app/src/web/core/app/constants.ts | 2 + projects/app/src/web/core/app/utils.ts | 2 +- .../app/src/web/core/dataset/constants.ts | 3 +- 207 files changed, 2943 insertions(+), 1378 deletions(-) create mode 100644 packages/service/core/app/templates/templateSchema.ts create mode 100644 packages/service/core/app/templates/templateTypeSchema.ts create mode 100644 packages/templates/README.md create mode 100644 packages/templates/package.json create mode 100644 packages/templates/register.ts rename {projects/app/public/appMarketTemplates => packages/templates/src}/CQ/template.json (100%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/Chinese/template.json (96%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/TranslateRobot/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/animalLife/template.json (97%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/chatGuide/template.json (100%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/divination/template.json (97%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/flux/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/githubIssue/template.json (96%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/google/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/longTranslate/template.json (97%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/plugin-dalle/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/plugin-feishu/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/simpleDatasetChat/template.json (100%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/srt-translate/template.json (99%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/stock/template.json (97%) rename {projects/app/public/appMarketTemplates => packages/templates/src}/timeBot/template.json (100%) create mode 100644 packages/templates/tsconfig.json create mode 100644 packages/templates/type.d.ts create mode 100644 packages/web/components/common/Icon/icons/common/arrowRight.svg create mode 100644 packages/web/components/common/Icon/icons/common/layer.svg create mode 100644 packages/web/components/common/Icon/icons/common/templateMarket.svg create mode 100644 packages/web/components/common/Icon/icons/common/thirdParty.svg create mode 100644 packages/web/components/common/Icon/icons/common/variable.svg rename projects/app/public/appMarketTemplates/TranslateRobot/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/TranslateRobot.svg (100%) rename projects/app/public/appMarketTemplates/animalLife/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/animalLife.svg (100%) rename projects/app/public/appMarketTemplates/Chinese/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/chinese.svg (100%) rename projects/app/public/appMarketTemplates/divination/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/divination.svg (100%) rename projects/app/public/appMarketTemplates/flux/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/flux.svg (100%) rename projects/app/public/appMarketTemplates/githubIssue/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/githubIssue.svg (100%) rename projects/app/public/appMarketTemplates/google/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/google.svg (100%) rename projects/app/public/appMarketTemplates/plugin-dalle/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/plugin-dalle.svg (100%) rename projects/app/public/appMarketTemplates/plugin-feishu/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/plugin-feishu.svg (100%) rename projects/app/public/appMarketTemplates/stock/avatar.svg => packages/web/components/common/Icon/icons/core/app/templates/stock.svg (100%) create mode 100644 packages/web/components/common/Icon/icons/support/account/laf.svg create mode 100644 packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/components/Variable.tsx delete mode 100644 packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.ts create mode 100644 packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.tsx create mode 100644 packages/web/i18n/en/account_thirdParty.json create mode 100644 packages/web/i18n/zh-CN/account_thirdParty.json create mode 100644 packages/web/i18n/zh-Hant/account_thirdParty.json create mode 100644 projects/app/src/components/common/Modal/UseGuideModal.tsx rename projects/app/src/pages/account/{info => thirdParty}/components/OpenAIAccountModal.tsx (52%) create mode 100644 projects/app/src/pages/account/thirdParty/components/WorkflowVariableModal.tsx create mode 100644 projects/app/src/pages/account/thirdParty/index.tsx create mode 100644 projects/app/src/pages/api/admin/initv4817.ts create mode 100644 projects/app/src/pages/api/support/user/team/thirtdParty/checkUsage.ts create mode 100644 projects/app/src/pages/app/detail/components/SimpleApp/components/ConfigToolModal.tsx create mode 100644 projects/app/src/pages/app/detail/components/SimpleApp/components/ToolSelect.tsx delete mode 100644 projects/app/src/service/core/app/template.ts diff --git a/README.md b/README.md index 5bbc8634f..483ab2b2f 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 多库复用,混用 - [x] chunk 记录修改和删除 - [x] 支持手动输入,直接分段,QA 拆分导入 - - [x] 支持 txt,md,html,pdf,docx,pptx,csv,xlsx (有需要更多可 PR file loader) - - [x] 支持 url 读取、CSV 批量导入 + - [x] 支持 txt,md,html,pdf,docx,pptx,csv,xlsx (有需要更多可 PR file loader),支持 url 读取、CSV 批量导入 - [x] 混合检索 & 重排 + - [x] API 知识库 - [ ] 自定义文件读取服务 - [ ] 自定义分块服务 @@ -69,7 +69,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 对话时反馈引用并可修改与删除 - [x] 完整上下文呈现 - [x] 完整模块中间值呈现 - - [x] 高级编排 DeBug 模式 + - [ ] 高级编排 DeBug 模式 `4` OpenAPI 接口 - [x] completions 接口 (chat 模式对齐 GPT 接口) diff --git a/docSite/content/zh-cn/docs/development/openapi/dataset.md b/docSite/content/zh-cn/docs/development/openapi/dataset.md index 15f5145a1..88877e3af 100644 --- a/docSite/content/zh-cn/docs/development/openapi/dataset.md +++ b/docSite/content/zh-cn/docs/development/openapi/dataset.md @@ -1424,7 +1424,11 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe "limit": 5000, "similarity": 0, "searchMode": "embedding", - "usingReRank": false + "usingReRank": false, + + "datasetSearchUsingExtensionQuery": true, + "datasetSearchExtensionModel": "gpt-4o-mini", + "datasetSearchExtensionBg": "" }' ``` @@ -1441,6 +1445,9 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe - similarity - 最低相关度(0~1,可选) - searchMode - 搜索模式:embedding | fullTextRecall | mixedRecall - usingReRank - 使用重排 +- datasetSearchUsingExtensionQuery - 使用问题优化 +- datasetSearchExtensionModel - 问题优化模型 +- datasetSearchExtensionBg - 问题优化背景描述 {{% /alert %}} {{< /markdownify >}} diff --git a/docSite/content/zh-cn/docs/development/upgrading/4817.md b/docSite/content/zh-cn/docs/development/upgrading/4817.md index 5f3729c2a..6f7b819f9 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4817.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4817.md @@ -7,10 +7,30 @@ toc: true weight: 807 --- +## 运行升级脚本 + +从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。 + +```bash +curl --location --request POST 'https://{{host}}/api/admin/initv4817' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` + +会将用户绑定的 OpenAI 账号移动到团队中。 ## 完整更新内容 -1. -2. 新增 - LLM 模型参数支持关闭 max_tokens 和 temperature。 -3. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。 -4. 优化 - 可用模型展示 \ No newline at end of file +1. 新增 - 简易模式工具调用支持数组类型插件。 +2. 新增 - 工作流增加异常离开自动保存,避免工作流丢失。 +3. 新增 - LLM 模型参数支持关闭 max_tokens 和 temperature。 +4. 新增 - 商业版支持后台配置模板市场。 +5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。 +6. 新增 - 搜索测试接口支持问题优化。 +7. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。 +8. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。 +9. 优化 - 可用模型展示 +10. 优化 - Mongo 查询语句,增加 virtual 字段。 +11. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。 +12. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。 +13. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。 \ No newline at end of file diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index c04276961..7cc24da41 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -18,6 +18,14 @@ export type NavbarItemType = { isActive: boolean; }; +export type ExternalProviderWorkflowVarType = { + name: string; + key: string; + intro: string; + isOpen: boolean; + url?: string; +}; + /* fastgpt main */ export type FastGPTConfigFileType = { feConfigs: FastGPTFeConfigsType; @@ -84,6 +92,7 @@ export type FastGPTFeConfigsType = { uploadFileMaxSize?: number; lafEnv?: string; navbarItems?: NavbarItemType[]; + externalProviderWorkflowVariables?: ExternalProviderWorkflowVarType[]; }; export type SystemEnvType = { diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 509672e69..86885c71e 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -13,6 +13,7 @@ import { StoreEdgeItemType } from '../workflow/type/edge'; import { AppPermission } from '../../support/permission/app/controller'; import { ParentIdType } from '../../common/parentFolder/type'; import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant'; +import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type'; export type AppSchema = { _id: string; @@ -184,3 +185,28 @@ export type SystemPluginListItemType = { name: string; avatar: string; }; + +export type AppTemplateSchemaType = { + templateId: string; + name: string; + intro: string; + avatar: string; + tags: string[]; + type: string; + author?: string; + isActive?: boolean; + userGuide?: { + type: 'markdown' | 'link'; + content?: string; + link?: string; + }; + isQuickTemplate?: boolean; + order?: number; + workflow: WorkflowTemplateBasicType; +}; + +export type TemplateTypeSchemaType = { + typeName: string; + typeId: string; + typeOrder: number; +}; diff --git a/packages/global/core/dataset/collection/utils.ts b/packages/global/core/dataset/collection/utils.ts index 97aec9ac9..f82a689c4 100644 --- a/packages/global/core/dataset/collection/utils.ts +++ b/packages/global/core/dataset/collection/utils.ts @@ -1,9 +1,7 @@ import { DatasetCollectionTypeEnum, TrainingModeEnum, TrainingTypeMap } from '../constants'; -import { CollectionWithDatasetType, DatasetCollectionSchemaType } from '../type'; +import { DatasetCollectionSchemaType } from '../type'; -export const getCollectionSourceData = ( - collection?: CollectionWithDatasetType | DatasetCollectionSchemaType -) => { +export const getCollectionSourceData = (collection?: DatasetCollectionSchemaType) => { return { sourceId: collection?.fileId || diff --git a/packages/global/core/dataset/type.d.ts b/packages/global/core/dataset/type.d.ts index e45a74463..1b4dae45c 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -133,11 +133,8 @@ export type DatasetTrainingSchemaType = { indexes: Omit[]; }; -export type CollectionWithDatasetType = Omit & { - datasetId: DatasetSchemaType; -}; -export type DatasetDataWithCollectionType = Omit & { - collectionId: DatasetCollectionSchemaType; +export type CollectionWithDatasetType = DatasetCollectionSchemaType & { + dataset: DatasetSchemaType; }; /* ================= dataset ===================== */ diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index d3dac3c10..4533db90e 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -21,13 +21,20 @@ import { ReadFileNodeResponse } from '../template/system/readFiles/type'; import { UserSelectOptionType } from '../template/system/userSelect/type'; import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type'; import { AiChatQuoteRoleType } from '../template/system/aiChat/type'; +import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type'; + +export type ExternalProviderType = { + openaiAccount?: OpenaiAccountType; + externalWorkflowVariables?: Record; +}; /* workflow props */ export type ChatDispatchProps = { res?: NextApiResponse; requestOrigin?: string; mode: 'test' | 'chat' | 'debug'; - user: UserModelSchema; + timezone: string; + externalProvider: ExternalProviderType; runningAppInfo: { id: string; // May be the id of the system plug-in (cannot be used directly to look up the table) diff --git a/packages/global/core/workflow/runtime/utils.ts b/packages/global/core/workflow/runtime/utils.ts index de815fe89..e58e7a3c1 100644 --- a/packages/global/core/workflow/runtime/utils.ts +++ b/packages/global/core/workflow/runtime/utils.ts @@ -9,6 +9,7 @@ import { isValidReferenceValueFormat } from '../utils'; import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io'; import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants'; +import { replaceVariable } from '../../../common/string/tools'; export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => { let limit = 10; @@ -317,6 +318,8 @@ export function replaceEditorVariable({ }) { if (typeof text !== 'string') return text; + text = replaceVariable(text, variables); + const variablePattern = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g; const matches = [...text.matchAll(variablePattern)]; if (matches.length === 0) return text; diff --git a/packages/global/core/workflow/template/system/workflowStart.ts b/packages/global/core/workflow/template/system/workflowStart.ts index 42d08b824..ef3250796 100644 --- a/packages/global/core/workflow/template/system/workflowStart.ts +++ b/packages/global/core/workflow/template/system/workflowStart.ts @@ -30,7 +30,6 @@ export const WorkflowStart: FlowNodeTemplateType = { intro: '', forbidDelete: true, unique: true, - courseUrl: '/docs/guide/workbench/workflow/input/', version: '481', inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }], outputs: [ diff --git a/packages/global/core/workflow/type/node.d.ts b/packages/global/core/workflow/type/node.d.ts index 2359ac0ee..d0bc5b919 100644 --- a/packages/global/core/workflow/type/node.d.ts +++ b/packages/global/core/workflow/type/node.d.ts @@ -69,6 +69,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & { diagram?: string; // diagram url courseUrl?: string; // course url + userGuide?: string; // user guide }; export type NodeTemplateListItemType = { @@ -87,6 +88,7 @@ export type NodeTemplateListItemType = { currentCost?: number; // 当前积分消耗 hasTokenFee?: boolean; // 是否配置积分 instructions?: string; // 使用说明 + courseUrl?: string; // 教程链接 }; export type NodeTemplateListType = { diff --git a/packages/global/support/outLink/type.d.ts b/packages/global/support/outLink/type.d.ts index 1b0447eb6..b1c74e60c 100644 --- a/packages/global/support/outLink/type.d.ts +++ b/packages/global/support/outLink/type.d.ts @@ -83,11 +83,6 @@ export type OutLinkSchema = { app: T; }; -// to handle MongoDB querying -export type OutLinkWithAppType = Omit & { - appId: AppSchema; -}; - // Edit the Outlink export type OutLinkEditType = { _id?: string; diff --git a/packages/global/support/permission/type.d.ts b/packages/global/support/permission/type.d.ts index f6f29c52a..619f8503e 100644 --- a/packages/global/support/permission/type.d.ts +++ b/packages/global/support/permission/type.d.ts @@ -1,5 +1,6 @@ +import { UserModelSchema } from '../user/type'; import { RequireOnlyOne } from '../../common/type/utils'; -import { TeamMemberWithUserSchema } from '../user/team/type'; +import { TeamMemberSchema } from '../user/team/type'; import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant'; import { MemberGroupSchemaType } from './memberGroup/type'; @@ -31,11 +32,7 @@ export type ResourcePermissionType = { }>; export type ResourcePerWithTmbWithUser = Omit & { - tmbId: TeamMemberWithUserSchema; -}; - -export type ResourcePerWithGroup = Omit & { - groupId: MemberGroupSchemaType; + tmbId: TeamMemberSchema & { user: UserModelSchema }; }; export type PermissionSchemaType = { diff --git a/packages/global/support/user/team/controller.d.ts b/packages/global/support/user/team/controller.d.ts index 69fcc6bb3..98a64af30 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -1,6 +1,6 @@ import { PermissionValueType } from '../../permission/type'; import { TeamMemberRoleEnum } from './constant'; -import { LafAccountType, TeamMemberSchema } from './type'; +import { LafAccountType, TeamMemberSchema, ThirdPartyAccountType } from './type'; export type AuthTeamRoleProps = { teamId: string; @@ -11,14 +11,13 @@ export type CreateTeamProps = { name: string; avatar?: string; defaultTeam?: boolean; - lafAccount?: LafAccountType; memberName?: string; }; -export type UpdateTeamProps = { +export type UpdateTeamProps = Omit & { name?: string; avatar?: string; teamDomain?: string; - lafAccount?: null | LafAccountType; + externalWorkflowVariable?: { key: string; value: string }; }; /* ------------- member ----------- */ diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index cce815cf0..9914e631a 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -4,6 +4,12 @@ import { LafAccountType } from './type'; import { PermissionValueType, ResourcePermissionType } from '../../permission/type'; import { TeamPermission } from '../../permission/user/controller'; +export type ThirdPartyAccountType = { + lafAccount?: LafAccountType; + openaiAccount?: OpenaiAccountType; + externalWorkflowVariables?: Record; +}; + export type TeamSchema = { _id: string; name: string; @@ -16,9 +22,8 @@ export type TeamSchema = { lastExportDatasetTime: Date; lastWebsiteSyncTime: Date; }; - lafAccount: LafAccountType; notificationAccount?: string; -}; +} & ThirdPartyAccountType; export type tagsType = { label: string; @@ -42,16 +47,9 @@ export type TeamMemberSchema = { defaultTeam: boolean; }; -export type TeamMemberWithUserSchema = Omit & { - userId: UserModelSchema; -}; - -export type TeamMemberWithTeamSchema = Omit & { - teamId: TeamSchema; -}; - -export type TeamMemberWithTeamAndUserSchema = Omit & { - userId: UserModelSchema; +export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & { + team: TeamSchema; + user: UserModelSchema; }; export type TeamTmbItemType = { @@ -66,10 +64,9 @@ export type TeamTmbItemType = { defaultTeam: boolean; role: `${TeamMemberRoleEnum}`; status: `${TeamMemberStatusEnum}`; - lafAccount?: LafAccountType; notificationAccount?: string; permission: TeamPermission; -}; +} & ThirdPartyAccountType; export type TeamMemberItemType = { userId: string; @@ -88,11 +85,16 @@ export type TeamTagItemType = { }; export type LafAccountType = { - token: string; appid: string; + token: string; pat: string; }; +export type OpenaiAccountType = { + key: string; + baseUrl: string; +}; + export type TeamInvoiceHeaderType = { teamName: string; unifiedCreditCode: string; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 1cb768dd3..0f8729266 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -14,10 +14,6 @@ export type UserModelSchema = { timezone: string; status: `${UserStatusEnum}`; lastLoginTmbId?: string; - openaiAccount?: { - key: string; - baseUrl: string; - }; fastgpt_sem?: { keyword: string; }; @@ -29,7 +25,6 @@ export type UserType = { avatar: string; timezone: string; promotionRate: UserModelSchema['promotionRate']; - openaiAccount: UserModelSchema['openaiAccount']; team: TeamTmbItemType; standardInfo?: standardInfoType; notificationAccount?: string; diff --git a/packages/plugins/src/searchXNG/template.json b/packages/plugins/src/searchXNG/template.json index ea6affb8b..639c19813 100644 --- a/packages/plugins/src/searchXNG/template.json +++ b/packages/plugins/src/searchXNG/template.json @@ -55,8 +55,7 @@ "maxFiles": 5, "canSelectFile": true, "canSelectImg": true, - "required": true, - "toolDescription": "部署的searXNG服务的链接" + "required": true } ], "outputs": [ diff --git a/packages/service/core/ai/config.ts b/packages/service/core/ai/config.ts index c30f5cc51..0db12a4b0 100644 --- a/packages/service/core/ai/config.ts +++ b/packages/service/core/ai/config.ts @@ -1,4 +1,3 @@ -import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import OpenAI from '@fastgpt/global/core/ai'; import { ChatCompletionCreateParamsNonStreaming, @@ -7,13 +6,11 @@ import { import { getErrText } from '@fastgpt/global/common/error/utils'; import { addLog } from '../../common/system/log'; import { i18nT } from '../../../web/i18n/utils'; +import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type'; export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1'; -export const getAIApi = (props?: { - userKey?: UserModelSchema['openaiAccount']; - timeout?: number; -}) => { +export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => { const { userKey, timeout } = props || {}; const baseUrl = @@ -29,7 +26,7 @@ export const getAIApi = (props?: { }); }; -export const getAxiosConfig = (props?: { userKey?: UserModelSchema['openaiAccount'] }) => { +export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => { const { userKey } = props || {}; const baseUrl = @@ -57,7 +54,7 @@ export const createChatCompletion = async ({ options }: { body: T; - userKey?: UserModelSchema['openaiAccount']; + userKey?: OpenaiAccountType; timeout?: number; options?: OpenAI.RequestOptions; }): Promise<{ diff --git a/packages/service/core/app/plugin/controller.ts b/packages/service/core/app/plugin/controller.ts index 00c2eea94..2d185686d 100644 --- a/packages/service/core/app/plugin/controller.ts +++ b/packages/service/core/app/plugin/controller.ts @@ -131,6 +131,7 @@ export async function getChildAppPreviewNode({ name: app.name, intro: app.intro, courseUrl: app.courseUrl, + userGuide: app.userGuide, showStatus: app.showStatus, isTool: true, version: app.version, diff --git a/packages/service/core/app/templates/templateSchema.ts b/packages/service/core/app/templates/templateSchema.ts new file mode 100644 index 000000000..7e61a1b86 --- /dev/null +++ b/packages/service/core/app/templates/templateSchema.ts @@ -0,0 +1,51 @@ +import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type'; +import { connectionMongo, getMongoModel } from '../../../common/mongo/index'; +const { Schema } = connectionMongo; + +export const collectionName = 'app_templates'; + +const AppTemplateSchema = new Schema({ + templateId: { + type: String, + required: true + }, + name: { + type: String + }, + intro: { + type: String + }, + avatar: { + type: String + }, + tags: { + type: [String], + default: undefined + }, + type: { + type: String + }, + isActive: { + type: Boolean + }, + userGuide: { + type: Object + }, + isQuickTemplate: { + type: Boolean + }, + order: { + type: Number, + default: -1 + }, + workflow: { + type: Object + } +}); + +AppTemplateSchema.index({ templateId: 1 }); + +export const MongoAppTemplate = getMongoModel( + collectionName, + AppTemplateSchema +); diff --git a/packages/service/core/app/templates/templateTypeSchema.ts b/packages/service/core/app/templates/templateTypeSchema.ts new file mode 100644 index 000000000..0926e8c6c --- /dev/null +++ b/packages/service/core/app/templates/templateTypeSchema.ts @@ -0,0 +1,25 @@ +import { TemplateTypeSchemaType } from '@fastgpt/global/core/app/type'; +import { connectionMongo, getMongoModel } from '../../../common/mongo/index'; +const { Schema } = connectionMongo; + +export const collectionName = 'app_template_types'; + +const TemplateTypeSchema = new Schema({ + typeName: { + type: String, + required: true + }, + typeId: { + type: String, + required: true + }, + typeOrder: { + type: Number, + default: 0 + } +}); + +export const MongoTemplateTypes = getMongoModel( + collectionName, + TemplateTypeSchema +); diff --git a/packages/service/core/app/version/schema.ts b/packages/service/core/app/version/schema.ts index 4acf91094..0836b950f 100644 --- a/packages/service/core/app/version/schema.ts +++ b/packages/service/core/app/version/schema.ts @@ -2,10 +2,16 @@ import { connectionMongo, getMongoModel, type Model } from '../../../common/mong const { Schema, model, models } = connectionMongo; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { chatConfigType } from '../schema'; +import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; export const AppVersionCollectionName = 'app_versions'; const AppVersionSchema = new Schema({ + tmbId: { + type: String, + ref: TeamMemberCollectionName, + required: true + }, appId: { type: Schema.Types.ObjectId, ref: AppVersionCollectionName, @@ -26,16 +32,8 @@ const AppVersionSchema = new Schema({ chatConfig: { type: chatConfigType }, - isPublish: { - type: Boolean - }, - versionName: { - type: String, - default: '' - }, - tmbId: { - type: String - } + isPublish: Boolean, + versionName: String }); try { diff --git a/packages/service/core/chat/utils.ts b/packages/service/core/chat/utils.ts index cbca1b1b3..ed35466e8 100644 --- a/packages/service/core/chat/utils.ts +++ b/packages/service/core/chat/utils.ts @@ -104,9 +104,6 @@ export const loadRequestMessages = async ({ }) => { // Load image to base64 const loadImageToBase64 = async (messages: ChatCompletionContentPart[]) => { - if (process.env.MULTIPLE_DATA_TO_BASE64 === 'false') { - return messages; - } return Promise.all( messages.map(async (item) => { if (item.type === 'image_url') { @@ -125,7 +122,7 @@ export const loadRequestMessages = async ({ try { // If imgUrl is a local path, load image from local, and set url to base64 - if (imgUrl.startsWith('/')) { + if (imgUrl.startsWith('/') || process.env.MULTIPLE_DATA_TO_BASE64 === 'true') { addLog.debug('Load image from local server', { baseUrl: serverRequestBaseUrl, requestUrl: imgUrl diff --git a/packages/service/core/dataset/collection/controller.ts b/packages/service/core/dataset/collection/controller.ts index 13281f679..6875fb546 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -4,11 +4,7 @@ import { } from '@fastgpt/global/core/dataset/constants'; import type { CreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d'; import { MongoDatasetCollection } from './schema'; -import { - CollectionWithDatasetType, - DatasetCollectionSchemaType, - DatasetSchemaType -} from '@fastgpt/global/core/dataset/type'; +import { DatasetCollectionSchemaType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type'; import { MongoDatasetTraining } from '../training/schema'; import { MongoDatasetData } from '../data/schema'; import { delImgByRelatedId } from '../../../common/file/image/controller'; @@ -230,7 +226,7 @@ export const delCollectionRelatedSource = async ({ collections, session }: { - collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[]; + collections: DatasetCollectionSchemaType[]; session: ClientSession; }) => { if (collections.length === 0) return; @@ -264,7 +260,7 @@ export async function delCollection({ session, delRelatedSource }: { - collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[]; + collections: DatasetCollectionSchemaType[]; session: ClientSession; delRelatedSource: boolean; }) { @@ -274,16 +270,7 @@ export async function delCollection({ if (!teamId) return Promise.reject('teamId is not exist'); - const datasetIds = Array.from( - new Set( - collections.map((item) => { - if (typeof item.datasetId === 'string') { - return String(item.datasetId); - } - return String(item.datasetId._id); - }) - ) - ); + const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId)))); const collectionIds = collections.map((item) => String(item._id)); // delete training data @@ -324,7 +311,7 @@ export async function delOnlyCollection({ collections, session }: { - collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[]; + collections: DatasetCollectionSchemaType[]; session: ClientSession; }) { if (collections.length === 0) return; @@ -333,16 +320,7 @@ export async function delOnlyCollection({ if (!teamId) return Promise.reject('teamId is not exist'); - const datasetIds = Array.from( - new Set( - collections.map((item) => { - if (typeof item.datasetId === 'string') { - return String(item.datasetId); - } - return String(item.datasetId._id); - }) - ) - ); + const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId)))); const collectionIds = collections.map((item) => String(item._id)); // delete training data diff --git a/packages/service/core/dataset/collection/schema.ts b/packages/service/core/dataset/collection/schema.ts index 73fc192e6..da13ed8ae 100644 --- a/packages/service/core/dataset/collection/schema.ts +++ b/packages/service/core/dataset/collection/schema.ts @@ -100,6 +100,13 @@ const DatasetCollectionSchema = new Schema({ } }); +DatasetCollectionSchema.virtual('dataset', { + ref: DatasetCollectionName, + localField: 'datasetId', + foreignField: '_id', + justOne: true +}); + try { // auth file DatasetCollectionSchema.index({ teamId: 1, fileId: 1 }); diff --git a/packages/service/core/dataset/collection/utils.ts b/packages/service/core/dataset/collection/utils.ts index 2f2ff4841..9bf9a2262 100644 --- a/packages/service/core/dataset/collection/utils.ts +++ b/packages/service/core/dataset/collection/utils.ts @@ -130,7 +130,7 @@ export const collectionTagsToTagLabel = async ({ }; export const syncCollection = async (collection: CollectionWithDatasetType) => { - const dataset = collection.datasetId; + const dataset = collection.dataset; if ( collection.type !== DatasetCollectionTypeEnum.link && @@ -183,7 +183,7 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => { teamId: collection.teamId, tmbId: collection.tmbId, name: collection.name, - datasetId: collection.datasetId._id, + datasetId: collection.datasetId, parentId: collection.parentId, type: collection.type, diff --git a/packages/service/core/dataset/controller.ts b/packages/service/core/dataset/controller.ts index d59ac7497..8ed6a3539 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -1,4 +1,4 @@ -import { CollectionWithDatasetType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type'; +import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type'; import { MongoDatasetCollection } from './collection/schema'; import { MongoDataset } from './schema'; import { delCollectionRelatedSource } from './collection/controller'; @@ -49,9 +49,9 @@ export async function findDatasetAndAllChildren({ } export async function getCollectionWithDataset(collectionId: string) { - const data = (await MongoDatasetCollection.findById(collectionId) - .populate('datasetId') - .lean()) as CollectionWithDatasetType; + const data = await MongoDatasetCollection.findById(collectionId) + .populate<{ dataset: DatasetSchemaType }>('dataset') + .lean(); if (!data) { return Promise.reject('Collection is not exist'); } diff --git a/packages/service/core/dataset/data/schema.ts b/packages/service/core/dataset/data/schema.ts index 4d5e6aa40..241f14285 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -77,21 +77,32 @@ const DatasetDataSchema = new Schema({ rebuilding: Boolean }); -// list collection and count data; list data; delete collection(relate data) -DatasetDataSchema.index({ - teamId: 1, - datasetId: 1, - collectionId: 1, - chunkIndex: 1, - updateTime: -1 +DatasetDataSchema.virtual('collection', { + ref: DatasetColCollectionName, + localField: 'collectionId', + foreignField: '_id', + justOne: true }); -// full text index -DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' }); -// Recall vectors after data matching -DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 }); -DatasetDataSchema.index({ updateTime: 1 }); -// rebuild data -DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 }); + +try { + // list collection and count data; list data; delete collection(relate data) + DatasetDataSchema.index({ + teamId: 1, + datasetId: 1, + collectionId: 1, + chunkIndex: 1, + updateTime: -1 + }); + // full text index + DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' }); + // Recall vectors after data matching + DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 }); + DatasetDataSchema.index({ updateTime: 1 }); + // rebuild data + DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 }); +} catch (error) { + console.log(error); +} export const MongoDatasetData = getMongoModel( DatasetDataCollectionName, diff --git a/packages/service/core/dataset/search/controller.ts b/packages/service/core/dataset/search/controller.ts index 0667a2b1e..4b87d2ca7 100644 --- a/packages/service/core/dataset/search/controller.ts +++ b/packages/service/core/dataset/search/controller.ts @@ -8,8 +8,8 @@ import { getVectorsByText } from '../../ai/embedding'; import { getVectorModel } from '../../ai/model'; import { MongoDatasetData } from '../data/schema'; import { + DatasetCollectionSchemaType, DatasetDataSchemaType, - DatasetDataWithCollectionType, SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { MongoDatasetCollection } from '../collection/schema'; @@ -267,7 +267,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { }); // get q and a - const dataList = (await MongoDatasetData.find( + const dataList = await MongoDatasetData.find( { teamId, datasetId: { $in: datasetIds }, @@ -276,8 +276,11 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { }, 'datasetId collectionId updateTime q a chunkIndex indexes' ) - .populate('collectionId', 'name fileId rawLink externalFileId externalFileUrl') - .lean()) as DatasetDataWithCollectionType[]; + .populate<{ collection: DatasetCollectionSchemaType }>( + 'collection', + 'name fileId rawLink externalFileId externalFileUrl' + ) + .lean(); // add score to data(It's already sorted. The first one is the one with the most points) const concatResults = dataList.map((data) => { @@ -307,8 +310,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) { a: data.a, chunkIndex: data.chunkIndex, datasetId: String(data.datasetId), - collectionId: String(data.collectionId?._id), - ...getCollectionSourceData(data.collectionId), + collectionId: String(data.collectionId), + ...getCollectionSourceData(data.collection), score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }] }; diff --git a/packages/service/core/dataset/training/controller.ts b/packages/service/core/dataset/training/controller.ts index 55b346dc7..fdae75d42 100644 --- a/packages/service/core/dataset/training/controller.ts +++ b/packages/service/core/dataset/training/controller.ts @@ -34,7 +34,7 @@ export const pushDataListToTrainingQueueByCollectionId = async ({ session?: ClientSession; } & PushDatasetDataProps) => { const { - datasetId: { _id: datasetId, agentModel, vectorModel } + dataset: { _id: datasetId, agentModel, vectorModel } } = await getCollectionWithDataset(collectionId); return pushDataListToTrainingQueue({ ...props, diff --git a/packages/service/core/workflow/dispatch/agent/classifyQuestion.ts b/packages/service/core/workflow/dispatch/agent/classifyQuestion.ts index 1558ee704..a3196e02b 100644 --- a/packages/service/core/workflow/dispatch/agent/classifyQuestion.ts +++ b/packages/service/core/workflow/dispatch/agent/classifyQuestion.ts @@ -35,7 +35,7 @@ type ActionProps = Props & { cqModel: LLMModelItemType }; /* request openai chat */ export const dispatchClassifyQuestion = async (props: Props): Promise => { const { - user, + externalProvider, node: { nodeId, name }, histories, params: { model, history = 6, agents, userChatInput } @@ -69,7 +69,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise item.key !== result.key) .map((item) => getHandleId(nodeId, 'source', item.key)), [DispatchNodeResponseKeyEnum.nodeResponse]: { - totalPoints: user.openaiAccount?.key ? 0 : totalPoints, + totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, model: modelName, query: userChatInput, tokens, @@ -80,7 +80,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise { @@ -131,7 +131,7 @@ const completions = async ({ }, cqModel ), - userKey: user.openaiAccount + userKey: externalProvider.openaiAccount }); const answer = data.choices?.[0].message?.content || ''; diff --git a/packages/service/core/workflow/dispatch/agent/extract.ts b/packages/service/core/workflow/dispatch/agent/extract.ts index 84e257b1f..ca59d1b42 100644 --- a/packages/service/core/workflow/dispatch/agent/extract.ts +++ b/packages/service/core/workflow/dispatch/agent/extract.ts @@ -46,7 +46,7 @@ const agentFunName = 'request_function'; export async function dispatchContentExtract(props: Props): Promise { const { - user, + externalProvider, node: { name }, histories, params: { content, history = 6, model, description, extractKeys } @@ -123,7 +123,7 @@ export async function dispatchContentExtract(props: Props): Promise { [NodeOutputKeyEnum.contextExtractFields]: JSON.stringify(arg), ...arg, [DispatchNodeResponseKeyEnum.nodeResponse]: { - totalPoints: user.openaiAccount?.key ? 0 : totalPoints, + totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, model: modelName, query: content, tokens, @@ -134,7 +134,7 @@ export async function dispatchContentExtract(props: Props): Promise { [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ { moduleName: name, - totalPoints: user.openaiAccount?.key ? 0 : totalPoints, + totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints, model: modelName, tokens } @@ -211,7 +211,7 @@ ${description ? `- ${description}` : ''} }; const toolChoice = async (props: ActionProps) => { - const { user, extractModel } = props; + const { externalProvider, extractModel } = props; const { filterMessages, agentFunction } = await getFunctionCallSchema(props); @@ -233,7 +233,7 @@ const toolChoice = async (props: ActionProps) => { }, extractModel ), - userKey: user.openaiAccount + userKey: externalProvider.openaiAccount }); const arg: Record = (() => { @@ -263,7 +263,7 @@ const toolChoice = async (props: ActionProps) => { }; const functionCall = async (props: ActionProps) => { - const { user, extractModel } = props; + const { externalProvider, extractModel } = props; const { agentFunction, filterMessages } = await getFunctionCallSchema(props); const functions: ChatCompletionCreateParams.Function[] = [agentFunction]; @@ -281,7 +281,7 @@ const functionCall = async (props: ActionProps) => { }, extractModel ), - userKey: user.openaiAccount + userKey: externalProvider.openaiAccount }); try { @@ -312,7 +312,7 @@ const functionCall = async (props: ActionProps) => { const completions = async ({ extractModel, - user, + externalProvider, histories, params: { content, extractKeys, description = 'No special requirements' } }: ActionProps) => { @@ -360,7 +360,7 @@ Human: ${content}` }, extractModel ), - userKey: user.openaiAccount + userKey: externalProvider.openaiAccount }); const answer = data.choices?.[0].message?.content || ''; diff --git a/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts b/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts index efdee7876..8818262a5 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts @@ -43,7 +43,7 @@ export const runToolWithFunctionCall = async ( requestOrigin, runtimeNodes, runtimeEdges, - user, + externalProvider, stream, workflowStreamResponse, params: { temperature, maxToken, aiChatVision } @@ -221,7 +221,7 @@ export const runToolWithFunctionCall = async ( getEmptyResponseTip } = await createChatCompletion({ body: requestBody, - userKey: user.openaiAccount, + userKey: externalProvider.openaiAccount, options: { headers: { Accept: 'application/json, text/plain, */*' diff --git a/packages/service/core/workflow/dispatch/agent/runTool/index.ts b/packages/service/core/workflow/dispatch/agent/runTool/index.ts index b70623189..0677a3b4f 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/index.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/index.ts @@ -46,7 +46,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< requestOrigin, chatConfig, runningAppInfo: { teamId }, - user, + externalProvider, params: { model, systemPrompt, @@ -153,7 +153,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< })(); // censor model and system key - if (toolModel.censor && !user.openaiAccount?.key) { + if (toolModel.censor && !externalProvider.openaiAccount?.key) { await postTextCensor({ text: `${systemPrompt} ${userChatInput} @@ -228,7 +228,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< tokens: toolNodeTokens, modelType: ModelTypeEnum.llm }); - const toolAIUsage = user.openaiAccount?.key ? 0 : totalPoints; + const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints; // flat child tool response const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat(); diff --git a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts index 882858249..4cb23676b 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts @@ -51,7 +51,7 @@ export const runToolWithPromptCall = async ( requestOrigin, runtimeNodes, runtimeEdges, - user, + externalProvider, stream, workflowStreamResponse, params: { temperature, maxToken, aiChatVision } @@ -230,7 +230,7 @@ export const runToolWithPromptCall = async ( getEmptyResponseTip } = await createChatCompletion({ body: requestBody, - userKey: user.openaiAccount, + userKey: externalProvider.openaiAccount, options: { headers: { Accept: 'application/json, text/plain, */*' diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts index 918db674d..b8a7f7e49 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts @@ -24,11 +24,9 @@ import { AIChatItemType } from '@fastgpt/global/core/chat/type'; import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils'; import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils'; import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools'; -import { addLog } from '../../../../../common/system/log'; import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants'; import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; -import { i18nT } from '../../../../../../web/i18n/utils'; type ToolRunResponseType = { toolRunResponse: DispatchFlowResponse; @@ -92,7 +90,7 @@ export const runToolWithToolChoice = async ( runtimeNodes, runtimeEdges, stream, - user, + externalProvider, workflowStreamResponse, params: { temperature, maxToken, aiChatVision } } = workflowProps; @@ -278,7 +276,7 @@ export const runToolWithToolChoice = async ( getEmptyResponseTip } = await createChatCompletion({ body: requestBody, - userKey: user.openaiAccount, + userKey: externalProvider.openaiAccount, options: { headers: { Accept: 'application/json, text/plain, */*' diff --git a/packages/service/core/workflow/dispatch/chat/oneapi.ts b/packages/service/core/workflow/dispatch/chat/oneapi.ts index 32003d6ce..5aa20d5e6 100644 --- a/packages/service/core/workflow/dispatch/chat/oneapi.ts +++ b/packages/service/core/workflow/dispatch/chat/oneapi.ts @@ -62,7 +62,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise { // censor model and system key - if (modelConstantsData.censor && !user.openaiAccount?.key) { + if (modelConstantsData.censor && !externalProvider.openaiAccount?.key) { return postTextCensor({ text: `${systemPrompt} ${userChatInput} @@ -170,7 +170,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise => { params, runtimeEdges, runtimeNodes, - user, node: { name } } = props; const { loopInputArray = [], childrenNodeIdList = [] } = params; @@ -86,14 +85,14 @@ export const dispatchLoop = async (props: Props): Promise => { return { [DispatchNodeResponseKeyEnum.assistantResponses]: assistantResponses, [DispatchNodeResponseKeyEnum.nodeResponse]: { - totalPoints: totalPoints, + totalPoints, loopInput: loopInputArray, loopResult: outputValueArr, loopDetail: loopDetail }, [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ { - totalPoints: user.openaiAccount?.key ? 0 : totalPoints, + totalPoints, moduleName: name } ], diff --git a/packages/service/core/workflow/dispatch/tools/runIfElse.ts b/packages/service/core/workflow/dispatch/tools/runIfElse.ts index 3d10698a2..ee66bd226 100644 --- a/packages/service/core/workflow/dispatch/tools/runIfElse.ts +++ b/packages/service/core/workflow/dispatch/tools/runIfElse.ts @@ -53,8 +53,8 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value [VariableConditionEnum.isEmpty]: () => isEmpty(inputValue), [VariableConditionEnum.isNotEmpty]: () => !isEmpty(inputValue), - [VariableConditionEnum.equalTo]: () => String(inputValue) === value, - [VariableConditionEnum.notEqual]: () => String(inputValue) !== value, + [VariableConditionEnum.equalTo]: () => String(inputValue).trim() === value.trim(), + [VariableConditionEnum.notEqual]: () => String(inputValue).trim() !== value.trim(), // number [VariableConditionEnum.greaterThan]: () => Number(inputValue) > Number(value), @@ -67,8 +67,8 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value [VariableConditionEnum.notInclude]: () => !isInclude(inputValue, value), // string - [VariableConditionEnum.startWith]: () => inputValue?.startsWith(value), - [VariableConditionEnum.endWith]: () => inputValue?.endsWith(value), + [VariableConditionEnum.startWith]: () => inputValue?.trim()?.startsWith(value), + [VariableConditionEnum.endWith]: () => inputValue?.trim()?.endsWith(value), [VariableConditionEnum.reg]: () => { if (typeof inputValue !== 'string' || !value) return false; if (value.startsWith('/')) { @@ -79,7 +79,7 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value } const reg = new RegExp(value, 'g'); - const result = reg.test(inputValue); + const result = reg.test(inputValue.trim()); return result; }, diff --git a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts index 302c49774..d0f0d64f4 100644 --- a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts +++ b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts @@ -19,7 +19,7 @@ type Props = ModuleDispatchProps<{ type Response = DispatchNodeResultType<{}>; export const dispatchUpdateVariable = async (props: Props): Promise => { - const { params, variables, runtimeNodes, workflowStreamResponse, node } = props; + const { params, variables, runtimeNodes, workflowStreamResponse, externalProvider } = props; const { updateList } = params; const nodeIds = runtimeNodes.map((node) => node.nodeId); @@ -41,15 +41,15 @@ export const dispatchUpdateVariable = async (props: Props): Promise => const value = (() => { // If first item is empty, it means it is a input value if (!item.value?.[0]) { - const formatValue = valueTypeFormat(item.value?.[1], item.valueType); - - return typeof formatValue === 'string' - ? replaceEditorVariable({ - text: formatValue, - nodes: runtimeNodes, - variables - }) - : formatValue; + const val = + typeof item.value?.[1] === 'string' + ? replaceEditorVariable({ + text: item.value?.[1], + nodes: runtimeNodes, + variables + }) + : item.value?.[1]; + return valueTypeFormat(val, item.valueType); } else { return getReferenceVariableValue({ value: item.value, @@ -80,7 +80,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise => workflowStreamResponse?.({ event: SseResponseEventEnum.updateVariables, - data: removeSystemVariable(variables) + data: removeSystemVariable(variables, externalProvider.externalWorkflowVariables) }); return { diff --git a/packages/service/core/workflow/dispatch/utils.ts b/packages/service/core/workflow/dispatch/utils.ts index 4186d89ac..55e68815c 100644 --- a/packages/service/core/workflow/dispatch/utils.ts +++ b/packages/service/core/workflow/dispatch/utils.ts @@ -14,6 +14,7 @@ import { NextApiResponse } from 'next'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { getNanoid } from '@fastgpt/global/common/string/tools'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; +import json5 from 'json5'; export const getWorkflowResponseWrite = ({ res, @@ -116,11 +117,18 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => { return Boolean(value); } try { - if (type === WorkflowIOValueTypeEnum.datasetQuote && !Array.isArray(value)) { - return JSON.parse(value); - } - if (type === WorkflowIOValueTypeEnum.selectDataset && !Array.isArray(value)) { - return JSON.parse(value); + if ( + type && + [ + WorkflowIOValueTypeEnum.object, + WorkflowIOValueTypeEnum.chatHistory, + WorkflowIOValueTypeEnum.datasetQuote, + WorkflowIOValueTypeEnum.selectApp, + WorkflowIOValueTypeEnum.selectDataset + ].includes(type) && + typeof value !== 'object' + ) { + return json5.parse(value); } } catch (error) { return value; @@ -141,14 +149,23 @@ export const checkQuoteQAValue = (quoteQA?: SearchDataResponseItemType[]) => { }; /* remove system variable */ -export const removeSystemVariable = (variables: Record) => { +export const removeSystemVariable = ( + variables: Record, + removeObj: Record = {} +) => { const copyVariables = { ...variables }; + delete copyVariables.userId; delete copyVariables.appId; delete copyVariables.chatId; delete copyVariables.responseChatItemId; delete copyVariables.histories; delete copyVariables.cTime; + // delete external provider workflow variables + Object.keys(removeObj).forEach((key) => { + delete copyVariables[key]; + }); + return copyVariables; }; export const filterSystemVariables = (variables: Record): SystemVariablesType => { diff --git a/packages/service/support/outLink/schema.ts b/packages/service/support/outLink/schema.ts index 4323a808c..7d1f1dbe3 100644 --- a/packages/service/support/outLink/schema.ts +++ b/packages/service/support/outLink/schema.ts @@ -83,6 +83,13 @@ const OutLinkSchema = new Schema({ } }); +OutLinkSchema.virtual('associatedApp', { + ref: AppCollectionName, + localField: 'appId', + foreignField: '_id', + justOne: true +}); + try { OutLinkSchema.index({ shareId: -1 }); } catch (error) { diff --git a/packages/service/support/permission/auth/team.ts b/packages/service/support/permission/auth/team.ts index 0a5589649..b659f2ffd 100644 --- a/packages/service/support/permission/auth/team.ts +++ b/packages/service/support/permission/auth/team.ts @@ -1,18 +1,32 @@ -import { TeamMemberWithUserSchema } from '@fastgpt/global/support/user/team/type'; import { MongoTeamMember } from '../../user/team/teamMemberSchema'; import { checkTeamAIPoints } from '../teamLimit'; import { UserErrEnum } from '@fastgpt/global/common/error/code/user'; +import { UserModelSchema } from '@fastgpt/global/support/user/type'; +import { TeamSchema } from '@fastgpt/global/support/user/team/type'; export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) { - const tmb = (await MongoTeamMember.findById(tmbId, 'teamId userId').populate( - 'userId', - 'timezone openaiAccount' - )) as TeamMemberWithUserSchema; + const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId') + .populate<{ user: UserModelSchema; team: TeamSchema }>([ + { + path: 'user', + select: 'timezone' + }, + { + path: 'team', + select: 'openaiAccount externalWorkflowVariables' + } + ]) + .lean(); + if (!tmb) return Promise.reject(UserErrEnum.unAuthUser); - await checkTeamAIPoints(tmb.teamId); + await checkTeamAIPoints(tmb.team._id); return { - user: tmb.userId + timezone: tmb.user.timezone, + externalProvider: { + openaiAccount: tmb.team.openaiAccount, + externalWorkflowVariables: tmb.team.externalWorkflowVariables + } }; } diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index c68f07d1b..458c05d01 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -10,17 +10,17 @@ import { MongoResourcePermission } from './schema'; import { ClientSession } from 'mongoose'; import { PermissionValueType, - ResourcePermissionType, - ResourcePerWithGroup, - ResourcePerWithTmbWithUser + ResourcePermissionType } from '@fastgpt/global/support/permission/type'; import { bucketNameMap } from '@fastgpt/global/common/file/constants'; import { addMinutes } from 'date-fns'; import { getGroupsByTmbId } from './memberGroup/controllers'; import { Permission } from '@fastgpt/global/support/permission/controller'; import { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; +import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type'; +import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; +import { UserModelSchema } from '@fastgpt/global/support/user/type'; /** get resource permission for a team member * If there is no permission for the team member, it will return undefined @@ -160,32 +160,33 @@ export const getClbsAndGroupsWithInfo = async ({ teamId: string; }) => Promise.all([ - (await MongoResourcePermission.find({ + MongoResourcePermission.find({ teamId, resourceId, resourceType, tmbId: { $exists: true } - }).populate({ - path: 'tmbId', - select: 'name userId', - populate: { - path: 'userId', - select: 'avatar' - } - })) as ResourcePerWithTmbWithUser[], - (await MongoResourcePermission.find({ + }) + .populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({ + path: 'tmb', + select: 'name userId', + populate: { + path: 'user', + select: 'avatar' + } + }) + .lean(), + MongoResourcePermission.find({ teamId, resourceId, resourceType, groupId: { $exists: true } - }).populate({ - path: 'groupId', - select: 'name avatar' - })) as ResourcePerWithGroup[] + }) + .populate<{ group: MemberGroupSchemaType }>('group', 'name avatar') + .lean() ]); export const delResourcePermissionById = (id: string) => { diff --git a/packages/service/support/permission/dataset/auth.ts b/packages/service/support/permission/dataset/auth.ts index dbe2c811b..08829fcf6 100644 --- a/packages/service/support/permission/dataset/auth.ts +++ b/packages/service/support/permission/dataset/auth.ts @@ -3,7 +3,6 @@ import { getResourcePermission, parseHeaderCert } from '../controller'; import { CollectionWithDatasetType, DatasetDataItemType, - DatasetFileSchema, DatasetSchemaType } from '@fastgpt/global/core/dataset/type'; import { getTmbInfoByTmbId } from '../../user/team/controller'; @@ -12,10 +11,6 @@ import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/per import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; import { getCollectionWithDataset } from '../../../core/dataset/controller'; -import { MongoDatasetCollection } from '../../../core/dataset/collection/schema'; -import { getFileById } from '../../../common/file/gridfs/controller'; -import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; -import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { MongoDatasetData } from '../../../core/dataset/data/schema'; import { AuthModeType, AuthResponseType } from '../type'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; @@ -181,7 +176,7 @@ export async function authDatasetCollection({ const { dataset } = await authDatasetByTmbId({ tmbId, - datasetId: collection.datasetId._id, + datasetId: collection.datasetId, per, isRoot: isRootFromHeader }); diff --git a/packages/service/support/permission/memberGroup/controllers.ts b/packages/service/support/permission/memberGroup/controllers.ts index c6027ffb9..78bae10c9 100644 --- a/packages/service/support/permission/memberGroup/controllers.ts +++ b/packages/service/support/permission/memberGroup/controllers.ts @@ -66,48 +66,19 @@ export const getGroupsByTmbId = async ({ }, ...(role ? { role: { $in: role } } : {}) }) - .populate('groupId') + .populate<{ group: MemberGroupSchemaType }>('group') .lean() - ).map((item) => { - return { - ...(item.groupId as any as MemberGroupSchemaType) - }; - }), - + ).map((item) => item.group), role ? [] : getTeamDefaultGroup({ teamId }) ]) ).flat(); -export const getTmbByGroupId = async (groupId: string) => { - return ( - await MongoGroupMemberModel.find({ - groupId - }) - .populate('tmbId') - .lean() - ).map((item) => { - return { - ...(item.tmbId as any as MemberGroupSchemaType) - }; - }); -}; - export const getGroupMembersByGroupId = async (groupId: string) => { return await MongoGroupMemberModel.find({ groupId }).lean(); }; -export const getGroupMembersWithInfoByGroupId = async (groupId: string) => { - return ( - await MongoGroupMemberModel.find({ - groupId - }) - .populate('tmbId') - .lean() - ).map((item) => item.tmbId) as any as TeamMemberSchema[]; // HACK: type casting -}; - /** * Get tmb's group permission: the maximum permission of the group * @param tmbId diff --git a/packages/service/support/permission/memberGroup/groupMemberSchema.ts b/packages/service/support/permission/memberGroup/groupMemberSchema.ts index bfd0a8af3..8f30d42dd 100644 --- a/packages/service/support/permission/memberGroup/groupMemberSchema.ts +++ b/packages/service/support/permission/memberGroup/groupMemberSchema.ts @@ -26,6 +26,13 @@ export const GroupMemberSchema = new Schema({ } }); +GroupMemberSchema.virtual('group', { + ref: MemberGroupCollectionName, + localField: 'groupId', + foreignField: '_id', + justOne: true +}); + try { GroupMemberSchema.index({ groupId: 1 diff --git a/packages/service/support/permission/schema.ts b/packages/service/support/permission/schema.ts index c2cf90a5a..ff5491d65 100644 --- a/packages/service/support/permission/schema.ts +++ b/packages/service/support/permission/schema.ts @@ -39,6 +39,19 @@ export const ResourcePermissionSchema = new Schema({ } }); +ResourcePermissionSchema.virtual('tmb', { + ref: TeamMemberCollectionName, + localField: 'tmbId', + foreignField: '_id', + justOne: true +}); +ResourcePermissionSchema.virtual('group', { + ref: MemberGroupCollectionName, + localField: 'groupId', + foreignField: '_id', + justOne: true +}); + try { ResourcePermissionSchema.index( { diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index 3594cfe5e..cf432b214 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -44,7 +44,6 @@ export async function getUserDetail({ avatar: user.avatar, timezone: user.timezone, promotionRate: user.promotionRate, - openaiAccount: user.openaiAccount, team: tmb, notificationAccount: tmb.notificationAccount, permission: tmb.permission diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index bc72a9089..ede448c93 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -1,4 +1,4 @@ -import { TeamTmbItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type'; +import { TeamSchema, TeamTmbItemType } from '@fastgpt/global/support/user/team/type'; import { ClientSession, Types } from '../../../common/mongo'; import { TeamMemberRoleEnum, @@ -15,37 +15,41 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema'; import { mongoSessionRun } from '../../../common/mongo/sessionRun'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; +import { getAIApi, openaiBaseUrl } from '../../../core/ai/config'; async function getTeamMember(match: Record): Promise { - const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema; + const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean(); if (!tmb) { return Promise.reject('member not exist'); } const Per = await getResourcePermission({ resourceType: PerResourceTypeEnum.team, - teamId: tmb.teamId._id, + teamId: tmb.teamId, tmbId: tmb._id }); return { userId: String(tmb.userId), - teamId: String(tmb.teamId._id), - teamName: tmb.teamId.name, + teamId: String(tmb.teamId), + teamName: tmb.team.name, memberName: tmb.name, - avatar: tmb.teamId.avatar, - balance: tmb.teamId.balance, + avatar: tmb.team.avatar, + balance: tmb.team.balance, tmbId: String(tmb._id), - teamDomain: tmb.teamId?.teamDomain, + teamDomain: tmb.team?.teamDomain, role: tmb.role, status: tmb.status, defaultTeam: tmb.defaultTeam, - lafAccount: tmb.teamId.lafAccount, permission: new TeamPermission({ per: Per ?? TeamDefaultPermissionVal, isOwner: tmb.role === TeamMemberRoleEnum.owner }), - notificationAccount: tmb.teamId.notificationAccount + notificationAccount: tmb.team.notificationAccount, + + lafAccount: tmb.team.lafAccount, + openaiAccount: tmb.team.openaiAccount, + externalWorkflowVariables: tmb.team.externalWorkflowVariables }; } @@ -145,21 +149,87 @@ export async function updateTeam({ name, avatar, teamDomain, - lafAccount + lafAccount, + openaiAccount, + externalWorkflowVariable }: UpdateTeamProps & { teamId: string }) { + // auth openai key + if (openaiAccount?.key) { + console.log('auth user openai key', openaiAccount?.key); + const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl; + openaiAccount.baseUrl = baseUrl; + + const ai = getAIApi({ + userKey: openaiAccount + }); + + const response = await ai.chat.completions.create({ + model: 'gpt-4o-mini', + max_tokens: 1, + messages: [{ role: 'user', content: 'hi' }] + }); + if (response?.choices?.[0]?.message?.content === undefined) { + return Promise.reject('Key response is empty'); + } + } + return mongoSessionRun(async (session) => { + const unsetObj = (() => { + const obj: Record = {}; + if (lafAccount?.pat === '') { + obj.lafAccount = 1; + } + if (openaiAccount?.key === '') { + obj.openaiAccount = 1; + } + if (externalWorkflowVariable) { + if (externalWorkflowVariable.value === '') { + obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] = 1; + } + } + + if (Object.keys(obj).length === 0) { + return undefined; + } + return { + $unset: obj + }; + })(); + const setObj = (() => { + const obj: Record = {}; + if (lafAccount?.pat && lafAccount?.appid) { + obj.lafAccount = lafAccount; + } + if (openaiAccount?.key && openaiAccount?.baseUrl) { + obj.openaiAccount = openaiAccount; + } + if (externalWorkflowVariable) { + if (externalWorkflowVariable.value !== '') { + obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] = + externalWorkflowVariable.value; + } + } + if (Object.keys(obj).length === 0) { + return undefined; + } + return obj; + })(); + await MongoTeam.findByIdAndUpdate( teamId, { - name, - avatar, - teamDomain, - lafAccount + $set: { + ...(name ? { name } : {}), + ...(avatar ? { avatar } : {}), + ...(teamDomain ? { teamDomain } : {}), + ...setObj + }, + ...unsetObj }, { session } ); - // update default group + // Update member group avatar if (avatar) { await MongoMemberGroupModel.updateOne( { diff --git a/packages/service/support/user/team/teamMemberSchema.ts b/packages/service/support/user/team/teamMemberSchema.ts index d14fb1b5b..c88eeb2a1 100644 --- a/packages/service/support/user/team/teamMemberSchema.ts +++ b/packages/service/support/user/team/teamMemberSchema.ts @@ -3,7 +3,6 @@ const { Schema } = connectionMongo; import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d'; import { userCollectionName } from '../../user/schema'; import { - TeamMemberRoleMap, TeamMemberStatusMap, TeamMemberCollectionName, TeamCollectionName @@ -42,6 +41,19 @@ const TeamMemberSchema = new Schema({ } }); +TeamMemberSchema.virtual('team', { + ref: TeamCollectionName, + localField: 'teamId', + foreignField: '_id', + justOne: true +}); +TeamMemberSchema.virtual('user', { + ref: userCollectionName, + localField: 'userId', + foreignField: '_id', + justOne: true +}); + try { TeamMemberSchema.index({ teamId: 1 }, { background: true }); TeamMemberSchema.index({ userId: 1 }, { background: true }); diff --git a/packages/service/support/user/team/teamSchema.ts b/packages/service/support/user/team/teamSchema.ts index 361a076b1..a418d0c34 100644 --- a/packages/service/support/user/team/teamSchema.ts +++ b/packages/service/support/user/team/teamSchema.ts @@ -47,6 +47,16 @@ const TeamSchema = new Schema({ type: String } }, + openaiAccount: { + type: { + key: String, + baseUrl: String + } + }, + externalWorkflowVariables: { + type: Object, + default: {} + }, notificationAccount: { type: String, required: false diff --git a/packages/service/type.d.ts b/packages/service/type.d.ts index 4ed702348..9fecfe205 100644 --- a/packages/service/type.d.ts +++ b/packages/service/type.d.ts @@ -9,7 +9,6 @@ import { import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type'; import { WorkerNameEnum, WorkerPool } from './worker/utils'; import { Worker } from 'worker_threads'; -import { TemplateMarketItemType } from '@fastgpt/global/core/workflow/type'; declare global { var systemInitBufferId: string | undefined; @@ -25,5 +24,4 @@ declare global { var reRankModels: ReRankModelItemType[]; var workerPoll: Record; - var appMarketTemplates: TemplateMarketItemType[]; } diff --git a/packages/templates/README.md b/packages/templates/README.md new file mode 100644 index 000000000..0ebd531dd --- /dev/null +++ b/packages/templates/README.md @@ -0,0 +1,3 @@ +# Package 说明 + +该 package 存放应用模板。 \ No newline at end of file diff --git a/packages/templates/package.json b/packages/templates/package.json new file mode 100644 index 000000000..447c38042 --- /dev/null +++ b/packages/templates/package.json @@ -0,0 +1,10 @@ +{ + "name": "@fastgpt/templates", + "version": "1.0.0", + "type": "module", + "dependencies": {}, + "devDependencies": { + "@fastgpt/global": "workspace:*", + "@fastgpt/service": "workspace:*" + } +} diff --git a/packages/templates/register.ts b/packages/templates/register.ts new file mode 100644 index 000000000..4a18d4d96 --- /dev/null +++ b/packages/templates/register.ts @@ -0,0 +1,83 @@ +import fs from 'fs'; +import path from 'path'; +import { isProduction } from '@fastgpt/global/common/system/constants'; +import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; +import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSchema'; +import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type'; + +const getTemplateNameList = () => { + const currentFileUrl = new URL(import.meta.url); + const templatesPath = path.join(path.dirname(currentFileUrl.pathname), 'src'); + + return fs.readdirSync(templatesPath) as string[]; +}; + +const getFileTemplates = (): AppTemplateSchemaType[] => { + const templateNames = getTemplateNameList(); + + const appMarketTemplates = templateNames.map((name) => { + const fileContent = require(`./src/${name}/template.json`); + + return { + ...fileContent, + templateId: `${PluginSourceEnum.community}-${name}`, + isActive: true + }; + }); + + return appMarketTemplates; +}; + +const getAppTemplates = async () => { + const communityTemplates = getFileTemplates(); + + const dbTemplates = await MongoAppTemplate.find(); + + // Merge db data to community templates + const communityTemplateConfig = communityTemplates.map((template) => { + const config = dbTemplates.find((t) => t.templateId === template.templateId); + + if (config) { + return { + ...template, + isActive: config.isActive ?? template.isActive, + tags: config.tags ?? template.tags, + userGuide: config.userGuide ?? template.userGuide, + isQuickTemplate: config.isQuickTemplate ?? template.isQuickTemplate, + order: config.order ?? template.order + }; + } + + return template; + }); + + const res = [ + ...communityTemplateConfig, + ...dbTemplates.filter((t) => !isCommunityTemplate(t.templateId)) + ].sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); + + return res; +}; + +export const getAppTemplatesAndLoadThem = async (refresh = false) => { + if (isProduction && global.appTemplates && global.appTemplates.length > 0 && !refresh) + return global.appTemplates; + + if (!global.appTemplates) { + global.appTemplates = []; + } + + try { + const appTemplates = await getAppTemplates(); + global.appTemplates = appTemplates; + return appTemplates; + } catch (error) { + // @ts-ignore + global.appTemplates = undefined; + return []; + } +}; + +export const isCommunityTemplate = (templateId: string) => { + return templateId.startsWith(PluginSourceEnum.community); +}; diff --git a/projects/app/public/appMarketTemplates/CQ/template.json b/packages/templates/src/CQ/template.json similarity index 100% rename from projects/app/public/appMarketTemplates/CQ/template.json rename to packages/templates/src/CQ/template.json diff --git a/projects/app/public/appMarketTemplates/Chinese/template.json b/packages/templates/src/Chinese/template.json similarity index 96% rename from projects/app/public/appMarketTemplates/Chinese/template.json rename to packages/templates/src/Chinese/template.json index 108c29bdd..7930229bb 100644 --- a/projects/app/public/appMarketTemplates/Chinese/template.json +++ b/packages/templates/src/Chinese/template.json @@ -2,9 +2,13 @@ "name": "汉语新解", "intro": "生成汉语释义图", "author": "", - "avatar": "/appMarketTemplates/Chinese/avatar.svg", + "avatar": "core/app/templates/chinese", "tags": ["roleplay"], "type": "advanced", + "userGuide": { + "type": "link", + "content": "https://mp.weixin.qq.com/s/T90-uZqGovYR90fST0o80Q" + }, "workflow": { "nodes": [ { diff --git a/projects/app/public/appMarketTemplates/TranslateRobot/template.json b/packages/templates/src/TranslateRobot/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/TranslateRobot/template.json rename to packages/templates/src/TranslateRobot/template.json index be0138de9..bd4eb1f3d 100644 --- a/projects/app/public/appMarketTemplates/TranslateRobot/template.json +++ b/packages/templates/src/TranslateRobot/template.json @@ -2,7 +2,7 @@ "name": "多轮翻译机器人", "intro": "通过 4 轮翻译,提高翻译英文的质量", "author": "", - "avatar": "/appMarketTemplates/TranslateRobot/avatar.svg", + "avatar": "core/app/templates/TranslateRobot", "tags": ["office-services"], "type": "advanced", "workflow": { diff --git a/projects/app/public/appMarketTemplates/animalLife/template.json b/packages/templates/src/animalLife/template.json similarity index 97% rename from projects/app/public/appMarketTemplates/animalLife/template.json rename to packages/templates/src/animalLife/template.json index 6350556a6..4706c1dbf 100644 --- a/projects/app/public/appMarketTemplates/animalLife/template.json +++ b/packages/templates/src/animalLife/template.json @@ -2,7 +2,7 @@ "name": "动物的一生", "intro": "使用AI生成任何事物的 “人生图”", "author": "", - "avatar": "/appMarketTemplates/animalLife/avatar.svg", + "avatar": "core/app/templates/animalLife", "tags": ["roleplay"], "type": "advanced", "workflow": { diff --git a/projects/app/public/appMarketTemplates/chatGuide/template.json b/packages/templates/src/chatGuide/template.json similarity index 100% rename from projects/app/public/appMarketTemplates/chatGuide/template.json rename to packages/templates/src/chatGuide/template.json diff --git a/projects/app/public/appMarketTemplates/divination/template.json b/packages/templates/src/divination/template.json similarity index 97% rename from projects/app/public/appMarketTemplates/divination/template.json rename to packages/templates/src/divination/template.json index 90f3e2c3b..fcfdf647f 100644 --- a/projects/app/public/appMarketTemplates/divination/template.json +++ b/packages/templates/src/divination/template.json @@ -2,7 +2,7 @@ "name": "周易占卜", "intro": "AI占卜,快来算算你的运势~", "author": "", - "avatar": "/appMarketTemplates/divination/avatar.svg", + "avatar": "core/app/templates/divination", "tags": ["roleplay"], "type": "advanced", "workflow": { diff --git a/projects/app/public/appMarketTemplates/flux/template.json b/packages/templates/src/flux/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/flux/template.json rename to packages/templates/src/flux/template.json index b23100b4f..d31e03e7c 100644 --- a/projects/app/public/appMarketTemplates/flux/template.json +++ b/packages/templates/src/flux/template.json @@ -2,7 +2,7 @@ "name": "Flux 绘图", "intro": "通过请求 Flux 接口绘图,需要有 api key", "author": "", - "avatar": "/appMarketTemplates/flux/avatar.svg", + "avatar": "core/app/templates/flux", "type": "plugin", "tags": ["image-generation"], "workflow": { diff --git a/projects/app/public/appMarketTemplates/githubIssue/template.json b/packages/templates/src/githubIssue/template.json similarity index 96% rename from projects/app/public/appMarketTemplates/githubIssue/template.json rename to packages/templates/src/githubIssue/template.json index 2a83e7366..e53aec743 100644 --- a/projects/app/public/appMarketTemplates/githubIssue/template.json +++ b/packages/templates/src/githubIssue/template.json @@ -2,9 +2,13 @@ "name": "GitHub Issue 总结机器人", "intro": "定时获取GitHub Issue信息,使用AI进行总结,并推送到飞书群中", "author": "", - "avatar": "/appMarketTemplates/githubIssue/avatar.svg", + "avatar": "core/app/templates/githubIssue", "tags": ["office-services"], "type": "advanced", + "userGuide": { + "type": "link", + "content": "https://mp.weixin.qq.com/s/CBrwSn1jQZO7ybsMSx5GnQ" + }, "workflow": { "nodes": [ { diff --git a/projects/app/public/appMarketTemplates/google/template.json b/packages/templates/src/google/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/google/template.json rename to packages/templates/src/google/template.json index 90adf4eaf..6097dee77 100644 --- a/projects/app/public/appMarketTemplates/google/template.json +++ b/packages/templates/src/google/template.json @@ -2,7 +2,7 @@ "name": "谷歌搜索", "intro": "通过请求谷歌搜索,查询相关内容作为模型的参考。", "author": "", - "avatar": "/appMarketTemplates/google/avatar.svg", + "avatar": "core/app/templates/google", "tags": ["recommendation", "web-search"], "type": "advanced", "workflow": { diff --git a/projects/app/public/appMarketTemplates/longTranslate/template.json b/packages/templates/src/longTranslate/template.json similarity index 97% rename from projects/app/public/appMarketTemplates/longTranslate/template.json rename to packages/templates/src/longTranslate/template.json index 8bad54944..9f9bcc913 100644 --- a/projects/app/public/appMarketTemplates/longTranslate/template.json +++ b/packages/templates/src/longTranslate/template.json @@ -1,10 +1,14 @@ { "name": "长文翻译专家", - "intro": "使用专有名称知识库协助翻译,更适合长文本的翻译机器人", + "intro": "使用专有名词知识库协助翻译,更适合长文本的翻译机器人", "author": "", - "avatar": "/appMarketTemplates/TranslateRobot/avatar.svg", + "avatar": "core/app/templates/TranslateRobot", "tags": ["office-services"], "type": "advanced", + "userGuide": { + "type": "link", + "content": "https://mp.weixin.qq.com/s/2kXdaSLOImhH1DTaFU8qXA" + }, "workflow": { "nodes": [ { diff --git a/projects/app/public/appMarketTemplates/plugin-dalle/template.json b/packages/templates/src/plugin-dalle/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/plugin-dalle/template.json rename to packages/templates/src/plugin-dalle/template.json index 53bb5533d..99261f7db 100644 --- a/projects/app/public/appMarketTemplates/plugin-dalle/template.json +++ b/packages/templates/src/plugin-dalle/template.json @@ -2,7 +2,7 @@ "name": "Dalle3 绘图", "intro": "通过请求 Dalle3 接口绘图,需要有 api key", "author": "", - "avatar": "/appMarketTemplates/plugin-dalle/avatar.svg", + "avatar": "core/app/templates/plugin-dalle", "type": "plugin", "tags": ["recommendation", "image-generation"], "workflow": { diff --git a/projects/app/public/appMarketTemplates/plugin-feishu/template.json b/packages/templates/src/plugin-feishu/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/plugin-feishu/template.json rename to packages/templates/src/plugin-feishu/template.json index 4cca190ef..09cc21b55 100644 --- a/projects/app/public/appMarketTemplates/plugin-feishu/template.json +++ b/packages/templates/src/plugin-feishu/template.json @@ -2,7 +2,7 @@ "name": "飞书 webhook 插件", "intro": "通过 webhook 给飞书机器人发送一条消息", "author": "", - "avatar": "/appMarketTemplates/plugin-feishu/avatar.svg", + "avatar": "core/app/templates/plugin-feishu", "type": "plugin", "tags": ["recommendation", "office-services"], "workflow": { diff --git a/projects/app/public/appMarketTemplates/simpleDatasetChat/template.json b/packages/templates/src/simpleDatasetChat/template.json similarity index 100% rename from projects/app/public/appMarketTemplates/simpleDatasetChat/template.json rename to packages/templates/src/simpleDatasetChat/template.json diff --git a/projects/app/public/appMarketTemplates/srt-translate/template.json b/packages/templates/src/srt-translate/template.json similarity index 99% rename from projects/app/public/appMarketTemplates/srt-translate/template.json rename to packages/templates/src/srt-translate/template.json index 66a9dc82a..43258360c 100644 --- a/projects/app/public/appMarketTemplates/srt-translate/template.json +++ b/packages/templates/src/srt-translate/template.json @@ -2,9 +2,13 @@ "name": "长字幕反思翻译机器人", "intro": "利用 AI 自我反思提升翻译质量,同时循环迭代执行 AI 工作流来突破 LLM tokens 限制,实现一个高效的长字幕翻译机器人", "author": "", - "avatar": "/appMarketTemplates/TranslateRobot/avatar.svg", + "avatar": "core/app/templates/TranslateRobot", "tags": ["office-services"], "type": "advanced", + "userGuide": { + "type": "link", + "content": "https://mp.weixin.qq.com/s/uztciEVvumNWNlct0IeJ-w" + }, "workflow": { "nodes": [ { diff --git a/projects/app/public/appMarketTemplates/stock/template.json b/packages/templates/src/stock/template.json similarity index 97% rename from projects/app/public/appMarketTemplates/stock/template.json rename to packages/templates/src/stock/template.json index cdde5c438..4a03d6794 100644 --- a/projects/app/public/appMarketTemplates/stock/template.json +++ b/packages/templates/src/stock/template.json @@ -2,7 +2,7 @@ "name": "利好大A", "intro": "", "author": "", - "avatar": "/appMarketTemplates/stock/avatar.svg", + "avatar": "core/app/templates/stock", "tags": ["roleplay"], "type": "advanced", "workflow": { diff --git a/projects/app/public/appMarketTemplates/timeBot/template.json b/packages/templates/src/timeBot/template.json similarity index 100% rename from projects/app/public/appMarketTemplates/timeBot/template.json rename to packages/templates/src/timeBot/template.json diff --git a/packages/templates/tsconfig.json b/packages/templates/tsconfig.json new file mode 100644 index 000000000..13c4daa3b --- /dev/null +++ b/packages/templates/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/templates/type.d.ts b/packages/templates/type.d.ts new file mode 100644 index 000000000..8fd24c7f0 --- /dev/null +++ b/packages/templates/type.d.ts @@ -0,0 +1,5 @@ +import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type'; + +declare global { + var appTemplates: AppTemplateSchemaType[]; +} diff --git a/packages/web/components/common/Avatar/index.tsx b/packages/web/components/common/Avatar/index.tsx index 9f398ac22..8b7abb30f 100644 --- a/packages/web/components/common/Avatar/index.tsx +++ b/packages/web/components/common/Avatar/index.tsx @@ -28,4 +28,4 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => { ); }; -export default Avatar; +export default React.memo(Avatar); diff --git a/packages/web/components/common/DndDrag/index.tsx b/packages/web/components/common/DndDrag/index.tsx index a072292ad..fc64f3a3e 100644 --- a/packages/web/components/common/DndDrag/index.tsx +++ b/packages/web/components/common/DndDrag/index.tsx @@ -57,4 +57,4 @@ function DndDrag({ children, renderClone, onDragEndCb, dataList }: Props) ); } -export default DndDrag; +export default React.memo(DndDrag) as (props: Props) => React.ReactElement; diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 2edf4cb6d..67b22d152 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -17,6 +17,7 @@ export const iconPaths = { 'common/addUser': () => import('./icons/common/addUser.svg'), 'common/administrator': () => import('./icons/common/administrator.svg'), 'common/arrowLeft': () => import('./icons/common/arrowLeft.svg'), + 'common/arrowRight': () => import('./icons/common/arrowRight.svg'), 'common/backFill': () => import('./icons/common/backFill.svg'), 'common/backLight': () => import('./icons/common/backLight.svg'), 'common/billing': () => import('./icons/common/billing.svg'), @@ -48,6 +49,7 @@ export const iconPaths = { 'common/language/China': () => import('./icons/common/language/China.svg'), 'common/language/en': () => import('./icons/common/language/en.svg'), 'common/language/zh': () => import('./icons/common/language/zh.svg'), + 'common/layer': () => import('./icons/common/layer.svg'), 'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'), 'common/line': () => import('./icons/common/line.svg'), 'common/lineChange': () => import('./icons/common/lineChange.svg'), @@ -81,13 +83,16 @@ export const iconPaths = { 'common/settingLight': () => import('./icons/common/settingLight.svg'), 'common/solidChevronRight': () => import('./icons/common/solidChevronRight.svg'), 'common/subtract': () => import('./icons/common/subtract.svg'), + 'common/templateMarket': () => import('./icons/common/templateMarket.svg'), 'common/text/t': () => import('./icons/common/text/t.svg'), + 'common/thirdParty': () => import('./icons/common/thirdParty.svg'), 'common/tickFill': () => import('./icons/common/tickFill.svg'), 'common/toolkit': () => import('./icons/common/toolkit.svg'), 'common/trash': () => import('./icons/common/trash.svg'), 'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'), 'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'), 'common/userInfo': () => import('./icons/common/userInfo.svg'), + 'common/variable': () => import('./icons/common/variable.svg'), 'common/viewLight': () => import('./icons/common/viewLight.svg'), 'common/voiceLight': () => import('./icons/common/voiceLight.svg'), 'common/warn': () => import('./icons/common/warn.svg'), @@ -119,6 +124,17 @@ export const iconPaths = { 'core/app/simpleMode/tts': () => import('./icons/core/app/simpleMode/tts.svg'), 'core/app/simpleMode/variable': () => import('./icons/core/app/simpleMode/variable.svg'), 'core/app/simpleMode/whisper': () => import('./icons/core/app/simpleMode/whisper.svg'), + 'core/app/templates/TranslateRobot': () => + import('./icons/core/app/templates/TranslateRobot.svg'), + 'core/app/templates/animalLife': () => import('./icons/core/app/templates/animalLife.svg'), + 'core/app/templates/chinese': () => import('./icons/core/app/templates/chinese.svg'), + 'core/app/templates/divination': () => import('./icons/core/app/templates/divination.svg'), + 'core/app/templates/flux': () => import('./icons/core/app/templates/flux.svg'), + 'core/app/templates/githubIssue': () => import('./icons/core/app/templates/githubIssue.svg'), + 'core/app/templates/google': () => import('./icons/core/app/templates/google.svg'), + 'core/app/templates/plugin-dalle': () => import('./icons/core/app/templates/plugin-dalle.svg'), + 'core/app/templates/plugin-feishu': () => import('./icons/core/app/templates/plugin-feishu.svg'), + 'core/app/templates/stock': () => import('./icons/core/app/templates/stock.svg'), 'core/app/toolCall': () => import('./icons/core/app/toolCall.svg'), 'core/app/ttsFill': () => import('./icons/core/app/ttsFill.svg'), 'core/app/type/httpPlugin': () => import('./icons/core/app/type/httpPlugin.svg'), @@ -376,6 +392,7 @@ export const iconPaths = { 'price/right': () => import('./icons/price/right.svg'), save: () => import('./icons/save.svg'), stop: () => import('./icons/stop.svg'), + 'support/account/laf': () => import('./icons/support/account/laf.svg'), 'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'), 'support/account/plans': () => import('./icons/support/account/plans.svg'), 'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'), diff --git a/packages/web/components/common/Icon/icons/common/arrowRight.svg b/packages/web/components/common/Icon/icons/common/arrowRight.svg new file mode 100644 index 000000000..05b639cbb --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/arrowRight.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/common/layer.svg b/packages/web/components/common/Icon/icons/common/layer.svg new file mode 100644 index 000000000..80d1998f5 --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/layer.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/common/templateMarket.svg b/packages/web/components/common/Icon/icons/common/templateMarket.svg new file mode 100644 index 000000000..b09774b9c --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/templateMarket.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/common/thirdParty.svg b/packages/web/components/common/Icon/icons/common/thirdParty.svg new file mode 100644 index 000000000..f6324f43b --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/thirdParty.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/common/variable.svg b/packages/web/components/common/Icon/icons/common/variable.svg new file mode 100644 index 000000000..2d2d89c7f --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/variable.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/projects/app/public/appMarketTemplates/TranslateRobot/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/TranslateRobot.svg similarity index 100% rename from projects/app/public/appMarketTemplates/TranslateRobot/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/TranslateRobot.svg diff --git a/projects/app/public/appMarketTemplates/animalLife/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/animalLife.svg similarity index 100% rename from projects/app/public/appMarketTemplates/animalLife/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/animalLife.svg diff --git a/projects/app/public/appMarketTemplates/Chinese/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/chinese.svg similarity index 100% rename from projects/app/public/appMarketTemplates/Chinese/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/chinese.svg diff --git a/projects/app/public/appMarketTemplates/divination/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/divination.svg similarity index 100% rename from projects/app/public/appMarketTemplates/divination/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/divination.svg diff --git a/projects/app/public/appMarketTemplates/flux/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/flux.svg similarity index 100% rename from projects/app/public/appMarketTemplates/flux/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/flux.svg diff --git a/projects/app/public/appMarketTemplates/githubIssue/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/githubIssue.svg similarity index 100% rename from projects/app/public/appMarketTemplates/githubIssue/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/githubIssue.svg diff --git a/projects/app/public/appMarketTemplates/google/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/google.svg similarity index 100% rename from projects/app/public/appMarketTemplates/google/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/google.svg diff --git a/projects/app/public/appMarketTemplates/plugin-dalle/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/plugin-dalle.svg similarity index 100% rename from projects/app/public/appMarketTemplates/plugin-dalle/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/plugin-dalle.svg diff --git a/projects/app/public/appMarketTemplates/plugin-feishu/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/plugin-feishu.svg similarity index 100% rename from projects/app/public/appMarketTemplates/plugin-feishu/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/plugin-feishu.svg diff --git a/projects/app/public/appMarketTemplates/stock/avatar.svg b/packages/web/components/common/Icon/icons/core/app/templates/stock.svg similarity index 100% rename from projects/app/public/appMarketTemplates/stock/avatar.svg rename to packages/web/components/common/Icon/icons/core/app/templates/stock.svg diff --git a/packages/web/components/common/Icon/icons/support/account/laf.svg b/packages/web/components/common/Icon/icons/support/account/laf.svg new file mode 100644 index 000000000..c908b6a8a --- /dev/null +++ b/packages/web/components/common/Icon/icons/support/account/laf.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Input/NumberInput/index.tsx b/packages/web/components/common/Input/NumberInput/index.tsx index 13a3a47e7..3e2a4564e 100644 --- a/packages/web/components/common/Input/NumberInput/index.tsx +++ b/packages/web/components/common/Input/NumberInput/index.tsx @@ -58,4 +58,4 @@ const MyNumberInput = (props: Props) => { ); }; -export default MyNumberInput; +export default React.memo(MyNumberInput); diff --git a/packages/web/components/common/Input/SearchInput/index.tsx b/packages/web/components/common/Input/SearchInput/index.tsx index 313f9b354..99affe0ca 100644 --- a/packages/web/components/common/Input/SearchInput/index.tsx +++ b/packages/web/components/common/Input/SearchInput/index.tsx @@ -13,4 +13,4 @@ const SearchInput = (props: InputProps) => { ); }; -export default SearchInput; +export default React.memo(SearchInput); diff --git a/packages/web/components/common/MyBox/index.tsx b/packages/web/components/common/MyBox/index.tsx index e5f8d70cd..501ff568a 100644 --- a/packages/web/components/common/MyBox/index.tsx +++ b/packages/web/components/common/MyBox/index.tsx @@ -17,4 +17,4 @@ const MyBox = ({ text, isLoading, children, size, ...props }: Props, ref: any) = ); }; -export default forwardRef(MyBox); +export default React.memo(forwardRef(MyBox)); diff --git a/packages/web/components/common/MyLoading/index.tsx b/packages/web/components/common/MyLoading/index.tsx index 9303b6482..a2664f764 100644 --- a/packages/web/components/common/MyLoading/index.tsx +++ b/packages/web/components/common/MyLoading/index.tsx @@ -44,4 +44,4 @@ const Loading = ({ ); }; -export default Loading; +export default React.memo(Loading); diff --git a/packages/web/components/common/MySlider/InputSlider.tsx b/packages/web/components/common/MySlider/InputSlider.tsx index db7f844f3..a8726e433 100644 --- a/packages/web/components/common/MySlider/InputSlider.tsx +++ b/packages/web/components/common/MySlider/InputSlider.tsx @@ -77,4 +77,4 @@ const InputSlider = ({ ); }; -export default InputSlider; +export default React.memo(InputSlider); diff --git a/packages/web/components/common/MyTooltip/QuestionTip.tsx b/packages/web/components/common/MyTooltip/QuestionTip.tsx index 506d22c64..f9d409003 100644 --- a/packages/web/components/common/MyTooltip/QuestionTip.tsx +++ b/packages/web/components/common/MyTooltip/QuestionTip.tsx @@ -15,4 +15,4 @@ const QuestionTip = ({ label, maxW, ...props }: Props) => { ); }; -export default QuestionTip; +export default React.memo(QuestionTip); diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariableLabelPlugin/components/VariableLabel.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariableLabelPlugin/components/VariableLabel.tsx index d8afc2749..3f99b4359 100644 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/VariableLabelPlugin/components/VariableLabel.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariableLabelPlugin/components/VariableLabel.tsx @@ -20,31 +20,22 @@ export default function VariableLabel({ {parentLabel !== 'undefined' ? ( - - + + {parentLabel} {childLabel} ) : ( - <> - {t('common:invalid_variable')} - + {t('common:invalid_variable')} )} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/components/Variable.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/components/Variable.tsx new file mode 100644 index 000000000..aa7813e58 --- /dev/null +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/components/Variable.tsx @@ -0,0 +1,28 @@ +import { Box, Flex } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; + +export default function Variable({ variableLabel }: { variableLabel: string }) { + const { t } = useTranslation(); + + return ( + <> + + {variableLabel ? ( + {variableLabel} + ) : ( + {t('common:invalid_variable')} + )} + + + ); +} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/index.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/index.tsx index bab875b0a..7d6911ede 100644 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/index.tsx @@ -5,8 +5,8 @@ import { useCallback, useEffect, useMemo } from 'react'; import { getHashtagRegexString } from './utils'; import { registerLexicalTextEntity } from '../../utils'; -import { $createVariableNode, VariableNode } from './node'; import { EditorVariablePickerType } from '../../type'; +import { $createVariableNode, VariableNode } from './node'; const REGEX = new RegExp(getHashtagRegexString(), 'i'); @@ -22,7 +22,11 @@ export default function VariablePlugin({ variables }: { variables: EditorVariabl }, [variables]); const createVariableNode = useCallback((textNode: TextNode): VariableNode => { - return $createVariableNode(textNode.getTextContent()); + const currentVariable = variables.find( + (item) => item.key === textNode.getTextContent().replace(/[{}]/g, '') + ); + const variableLabel = currentVariable?.label; + return $createVariableNode(textNode.getTextContent(), variableLabel || ''); }, []); const getVariableMatch = useCallback((text: string) => { diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.ts b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.ts deleted file mode 100644 index 536f25b90..000000000 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { NodeKey, EditorConfig, LexicalNode, SerializedTextNode } from 'lexical'; - -import { TextNode, $applyNodeReplacement } from 'lexical'; -import { addClassNamesToElement } from '@lexical/utils'; -import styles from '../../index.module.scss'; - -export class VariableNode extends TextNode { - static getType(): string { - return 'variable'; - } - - static clone(node: VariableNode): VariableNode { - return new VariableNode(node.__text, node.__key); - } - - constructor(text: string, key?: NodeKey) { - super(text, key); - } - - createDOM(config: EditorConfig): HTMLElement { - const element = super.createDOM(config); - addClassNamesToElement(element, styles.variable); - return element; - } - - static importJSON(serializedNode: SerializedTextNode): TextNode { - const node = $createVariableNode(serializedNode.text); - node.setFormat(serializedNode.format); - node.setDetail(serializedNode.detail); - node.setMode(serializedNode.mode); - node.setStyle(serializedNode.style); - return node; - } - - exportJSON(): SerializedTextNode { - return { - ...super.exportJSON(), - type: 'variable' - }; - } -} - -export function $createVariableNode(text: string): VariableNode { - return $applyNodeReplacement(new VariableNode(text)); -} - -export function $isVariableNode(node: LexicalNode | null | undefined): node is VariableNode { - return node instanceof VariableNode; -} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.tsx new file mode 100644 index 000000000..49bcfee8d --- /dev/null +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePlugin/node.tsx @@ -0,0 +1,105 @@ +import { + DecoratorNode, + DOMConversionMap, + DOMExportOutput, + EditorConfig, + LexicalEditor, + LexicalNode, + NodeKey, + SerializedLexicalNode, + Spread, + TextFormatType +} from 'lexical'; +import Variable from './components/Variable'; + +export type SerializedVariableNode = Spread< + { + variableKey: string; + variableLabel: string; + format: number | TextFormatType; + }, + SerializedLexicalNode +>; + +export class VariableNode extends DecoratorNode { + __format: number | TextFormatType; + __variableKey: string; + __variableLabel: string; + static getType(): string { + return 'Variable'; + } + static clone(node: VariableNode): VariableNode { + return new VariableNode(node.__variableKey, node.__variableLabel, node.__format, node.__key); + } + constructor( + variableKey: string, + variableLabel: string, + format?: number | TextFormatType, + key?: NodeKey + ) { + super(key); + this.__variableKey = variableKey; + this.__format = format || 0; + this.__variableLabel = variableLabel; + } + + static importJSON(serializedNode: SerializedVariableNode): VariableNode { + const node = $createVariableNode(serializedNode.variableKey, serializedNode.variableLabel); + node.setFormat(serializedNode.format); + return node; + } + + setFormat(format: number | TextFormatType): void { + const self = this.getWritable(); + self.__format = format; + } + getFormat(): number | TextFormatType { + return this.__format; + } + + exportJSON(): SerializedVariableNode { + return { + format: this.__format || 0, + type: 'Variable', + version: 1, + variableKey: this.getVariableKey(), + variableLabel: this.__variableLabel + }; + } + createDOM(): HTMLElement { + const element = document.createElement('span'); + return element; + } + exportDOM(): DOMExportOutput { + const element = document.createElement('span'); + return { element }; + } + static importDOM(): DOMConversionMap | null { + return {}; + } + updateDOM(): false { + return false; + } + getVariableKey(): string { + return this.__variableKey; + } + getTextContent( + _includeInert?: boolean | undefined, + _includeDirectionless?: false | undefined + ): string { + return `${this.__variableKey}`; + } + decorate(_editor: LexicalEditor, config: EditorConfig): JSX.Element { + return ; + } +} + +export function $createVariableNode(variableKey: string, variableLabel: string): VariableNode { + return new VariableNode(variableKey, variableLabel); +} + +export function $isVariableNode( + node: VariableNode | LexicalNode | null | undefined +): node is VariableNode { + return node instanceof VariableNode; +} diff --git a/packages/web/components/common/Textarea/PromptEditor/utils.ts b/packages/web/components/common/Textarea/PromptEditor/utils.ts index f1689190c..15ff217f0 100644 --- a/packages/web/components/common/Textarea/PromptEditor/utils.ts +++ b/packages/web/components/common/Textarea/PromptEditor/utils.ts @@ -11,8 +11,9 @@ import type { EntityMatch } from '@lexical/text'; import { $createTextNode, $getRoot, $isTextNode, TextNode } from 'lexical'; import { useCallback } from 'react'; import { VariableLabelNode } from './plugins/VariableLabelPlugin/node'; +import { VariableNode } from './plugins/VariablePlugin/node'; -export function registerLexicalTextEntity( +export function registerLexicalTextEntity( editor: LexicalEditor, getMatch: (text: string) => null | EntityMatch, targetNode: Klass, @@ -22,7 +23,7 @@ export function registerLexicalTextEntity { + const replaceWithSimpleText = (node: TextNode | VariableLabelNode | VariableNode): void => { const textNode = $createTextNode(node.getTextContent()); textNode.setFormat(node.getFormat()); node.replace(textNode); @@ -228,6 +229,8 @@ export function editorStateToText(editor: LexicalEditor) { paragraphText.push(child.text); } else if (child.type === 'variableLabel') { paragraphText.push(child.variableKey); + } else if (child.type === 'Variable') { + paragraphText.push(child.variableKey); } }); editorStateTextString.push(paragraphText.join('')); diff --git a/packages/web/core/workflow/constants.ts b/packages/web/core/workflow/constants.ts index 9d0cf6939..61e27d2cb 100644 --- a/packages/web/core/workflow/constants.ts +++ b/packages/web/core/workflow/constants.ts @@ -1,6 +1,8 @@ import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { i18nT } from '../../i18n/utils'; import type { PluginGroupSchemaType, TGroupType } from '../../../service/core/app/plugin/type'; +import { AppTemplateTypeEnum } from '@fastgpt/global/core/app/constants'; +import { TemplateTypeSchemaType } from '@fastgpt/global/core/app/type'; export const workflowNodeTemplateList = [ { @@ -79,3 +81,31 @@ export const defaultGroup: PluginGroupSchemaType = { groupOrder: 0, groupTypes: systemPluginTemplateList }; + +export const defaultTemplateTypes: TemplateTypeSchemaType[] = [ + { + typeName: i18nT('common:app.templateMarket.templateTags.Writing'), + typeId: AppTemplateTypeEnum.writing, + typeOrder: 0 + }, + { + typeName: i18nT('common:app.templateMarket.templateTags.Image_generation'), + typeId: AppTemplateTypeEnum.imageGeneration, + typeOrder: 1 + }, + { + typeName: i18nT('common:app.templateMarket.templateTags.Web_search'), + typeId: AppTemplateTypeEnum.webSearch, + typeOrder: 2 + }, + { + typeName: i18nT('common:app.templateMarket.templateTags.Roleplay'), + typeId: AppTemplateTypeEnum.roleplay, + typeOrder: 3 + }, + { + typeName: i18nT('common:app.templateMarket.templateTags.Office_services'), + typeId: AppTemplateTypeEnum.officeServices, + typeOrder: 4 + } +]; diff --git a/packages/web/i18n/en/account.json b/packages/web/i18n/en/account.json index 28f3369ad..b115fa506 100644 --- a/packages/web/i18n/en/account.json +++ b/packages/web/i18n/en/account.json @@ -9,5 +9,6 @@ "personalization": "Personalization", "promotion_records": "Promotions", "team": "Team", + "third_party": "Third Party", "usage_records": "Usage" } diff --git a/packages/web/i18n/en/account_info.json b/packages/web/i18n/en/account_info.json index 09f4809a5..dbda14415 100644 --- a/packages/web/i18n/en/account_info.json +++ b/packages/web/i18n/en/account_info.json @@ -42,7 +42,6 @@ "notification_receiving": "Notify", "notification_receiving_hint": "Notification reception", "old_password": "Old Password", - "open_api_notice": "You can fill in the relevant key of OpenAI/OneAPI. \nIf you fill in this content, the online platform using [AI Dialogue], [Problem Classification] and [Content Extraction] will use the Key you filled in, and there will be no charge. \nPlease pay attention to whether your Key has permission to access the corresponding model. \nGPT models can choose FastAI.", "openai_account_configuration": "OpenAI account configuration", "openai_account_setting_exception": "Setting OpenAI account exception", "package_and_usage": "Plans", @@ -60,7 +59,6 @@ "please_bind_notification_receiving_path": "Please bind the notification receiving method first", "purchase_extra_package": "Upgrade", "reminder_create_bound_notification_account": "Remind the creator to bind the notification account", - "request_address_notice": "Request address, default is openai official. \nThe forwarding address can be filled in, but \"v1\" is not automatically completed.", "resource_usage": "Usages", "select_avatar": "Click to select avatar", "standard_package_and_extra_resource_package": "Includes standard and extra plans", diff --git a/packages/web/i18n/en/account_thirdParty.json b/packages/web/i18n/en/account_thirdParty.json new file mode 100644 index 000000000..7b3424cc5 --- /dev/null +++ b/packages/web/i18n/en/account_thirdParty.json @@ -0,0 +1,16 @@ +{ + "configured": "Configured", + "error.no_permission": "Please contact the administrator to configure", + "laf_account": "laf account", + "no_intro": "No explanation yet", + "not_configured": "Not configured", + "open_api_notice": "You can fill in the relevant key of OpenAI/OneAPI. \nIf you fill in this content, the online platform using [AI Dialogue], [Problem Classification] and [Content Extraction] will use the Key you filled in, and there will be no charge. \nPlease pay attention to whether your Key has permission to access the corresponding model. \nGPT models can choose FastAI.", + "openai_account_configuration": "OpenAI/OneAPI account", + "request_address_notice": "Request address, default is openai official. \nThe forwarding address can be filled in, but \\\"v1\\\" is not automatically completed.", + "third_party_account": "Third-party account", + "third_party_account_desc": "The administrator can configure third-party accounts or variables here, and the account will be used by all team members.", + "unavailable": "Get usage exception", + "usage": "Usage", + "value_not_return_tip": "After the parameters are configured, they will not return to the front end again and do not need to be leaked to other members.", + "value_placeholder": "Enter parameter values. \nEntering a null value means deleting the configuration." +} diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 4d5de7d24..8e1d2ca10 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -80,11 +80,7 @@ "mark_count": "Number of Marked Answers", "max_histories_number": "Max histories", "max_histories_number_tip": "The maximum number of rounds of dialogue that the model can carry into memory. If the memory exceeds the model context, the system will force truncation. \nTherefore, even if 30 rounds of dialogue are configured, the actual number may not reach 30 rounds during operation.", - "max_quote_tokens": "Max quote", - "max_quote_tokens_tips": "The maximum number of tokens in a single search, about 1 character in Chinese = 1.7 tokens, and about 1 character in English = 1 token", "max_tokens": "Max tokens", - "min_similarity": "lowest correlation", - "min_similarity_tip": "The relevance of different index models is different. Please select the appropriate value through search testing. \nWhen using Result Rearrange , use the rearranged results for filtering.", "module.Custom Title Tip": "This title will be displayed during the conversation.", "module.No Modules": "No Plugins Found", "module.type": "\"{{type}}\" type\n{{description}}", @@ -113,6 +109,7 @@ "search_app": "Search Application", "setting_app": "Workflow", "setting_plugin": "Workflow", + "simple_tool_tips": "This plugin contains special inputs and is not currently supported for invocation by simple applications.", "stream_response": "Stream", "stream_response_tip": "Turning this switch off forces the model to use non-streaming mode and will not output content directly. \nIn the output of the AI ​​reply, the content output by this model can be obtained for secondary processing.", "temperature": "Temperature", @@ -135,6 +132,7 @@ "templateMarket.templateTags.Roleplay": "Roleplay", "templateMarket.templateTags.Web_search": "Web Search", "templateMarket.templateTags.Writing": "Writing", + "templateMarket.template_guide": "Guide", "template_market": "Templates", "template_market_description": "Explore more features in the template market, with configuration tutorials and usage guides to help you understand and get started with various applications.", "template_market_empty_data": "No suitable templates found", @@ -155,7 +153,6 @@ "type.Plugin": "Plugin", "type.Simple bot": "Simple App", "type.Workflow bot": "Workflow", - "unusual_leave_auto_save": "Abnormal exit, automatic saving", "upload_file_max_amount": "Maximum File Quantity", "upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation", "variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 7b04bd544..3cae5c851 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -32,6 +32,7 @@ "Warning": "Warning", "add_new": "Add New", "add_new_param": "Add new param", + "app.templateMarket.templateTags.Writing": "Writing", "back": "Back", "chose_condition": "Choose Condition", "chosen": "Chosen", @@ -683,6 +684,7 @@ "core.module.Variable": "Global Variable", "core.module.Variable Setting": "Variable Setting", "core.module.edit.Field Name Cannot Be Empty": "Field Name Cannot Be Empty", + "core.module.edit.Field Value Type Cannot Be Empty": "Optional data type cannot be empty.", "core.module.extract.Add field": "Add Field", "core.module.extract.Enum Description": "List the possible values of this field, one per line", "core.module.extract.Enum Value": "Enum Value", @@ -896,6 +898,10 @@ "item_name": "Field Name", "just_now": "just", "key_repetition": "Key Repetition", + "max_quote_tokens": "Quote cap", + "max_quote_tokens_tips": "The maximum number of tokens in a single search, about 1 character in Chinese = 1.7 tokens, and about 1 character in English = 1 token", + "min_similarity": "lowest correlation", + "min_similarity_tip": "The relevance of different index models is different. Please select the appropriate value through search testing. \nWhen using Result Rearrange , use the rearranged results for filtering.", "model.billing": "Billing", "model.model_type": "Model type", "model.name": "Model name", diff --git a/packages/web/i18n/zh-CN/account.json b/packages/web/i18n/zh-CN/account.json index 4da8699f1..85b0e4954 100644 --- a/packages/web/i18n/zh-CN/account.json +++ b/packages/web/i18n/zh-CN/account.json @@ -9,5 +9,6 @@ "personalization": "个性化", "promotion_records": "促销记录", "team": "团队管理", + "third_party": "第三方账号", "usage_records": "使用记录" } diff --git a/packages/web/i18n/zh-CN/account_info.json b/packages/web/i18n/zh-CN/account_info.json index 816ec8628..6059523dd 100644 --- a/packages/web/i18n/zh-CN/account_info.json +++ b/packages/web/i18n/zh-CN/account_info.json @@ -42,9 +42,6 @@ "notification_receiving": "通知接收", "notification_receiving_hint": "通知接收", "old_password": "旧密码", - "open_api_notice": "可以填写 OpenAI/OneAPI 的相关密钥。如果你填写了该内容,在线上平台使用【 AI 对话】、【问题分类】和【内容提取】将会走你填写的 Key,不会计费。请注意你的 Key 是否有访问对应模型的权限。 GPT 模型可以选择 FastAI 。", - "openai_account_configuration": "OpenAI 账号配置", - "openai_account_setting_exception": "设置 OpenAI 账号异常", "package_and_usage": "套餐与用量", "package_details": "套餐详情", "package_expiry_time": "套餐到期时间", @@ -60,7 +57,6 @@ "please_bind_notification_receiving_path": "请先绑定通知接收途径", "purchase_extra_package": "购买额外套餐", "reminder_create_bound_notification_account": "提醒创建者绑定通知账号", - "request_address_notice": "请求地址,默认为 openai 官方。可填中转地址,未自动补全 \"v1\"", "resource_usage": "资源用量", "select_avatar": "点击选择头像", "standard_package_and_extra_resource_package": "包含标准套餐与额外资源包", diff --git a/packages/web/i18n/zh-CN/account_thirdParty.json b/packages/web/i18n/zh-CN/account_thirdParty.json new file mode 100644 index 000000000..b3dcf2c9f --- /dev/null +++ b/packages/web/i18n/zh-CN/account_thirdParty.json @@ -0,0 +1,20 @@ +{ + "configured": "已配置", + "error.no_permission": "请联系管理员配置", + "get_usage_failed": "获取使用量失败", + "laf_account": "laf 账号", + "no_intro": "暂无说明", + "not_configured": "未配置", + "open_api_notice": "可以填写 OpenAI/OneAPI 的相关密钥。如果你填写了该内容,在线上平台使用【 AI 对话】、【问题分类】和【内容提取】将会走你填写的 Key,不会计费。请注意你的 Key 是否有访问对应模型的权限。 GPT 模型可以选择 FastAI 。", + "openai_account_configuration": "OpenAI/OneAPI 账号", + "openai_account_setting_exception": "设置 OpenAI 账号异常", + "request_address_notice": "请求地址,默认为 openai 官方。可填中转地址,未自动补全 \"v1\"", + "third_party_account": "第三方账号", + "third_party_account.configured": "已配置", + "third_party_account.not_configured": "未配置", + "third_party_account_desc": "管理员可以在这里配置第三方账号或变量,该账号将被团队所有人使用", + "unavailable": "获取使用量异常", + "usage": "使用量:", + "value_not_return_tip": "参数配置后,不会再次返回前端,无需泄露给其他成员", + "value_placeholder": "输入参数值。输入空值表示删除该配置。" +} diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index 0954c63b2..4124839cb 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -80,11 +80,7 @@ "mark_count": "标注答案数量", "max_histories_number": "记忆轮数", "max_histories_number_tip": "模型最多携带多少轮对话进入记忆中,如果记忆超出模型上下文,系统会强制截断。所以尽管配置 30 轮对话,实际运行时候,不一定会达到 30 轮。", - "max_quote_tokens": "引用上限", - "max_quote_tokens_tips": "单次搜索最大的 token 数量,中文约 1 字=1.7 tokens,英文约 1 字=1 token", "max_tokens": "回复上限", - "min_similarity": "最低相关度", - "min_similarity_tip": "不同索引模型的相关度有区别,请通过搜索测试来选择合适的数值。使用 结果重排 时,使用重排结果进行过滤。", "module.Custom Title Tip": "该标题名字会展示在对话过程中", "module.No Modules": "没找到插件", "module.type": "\"{{type}}\"类型\n{{description}}", @@ -113,6 +109,7 @@ "search_app": "搜索应用", "setting_app": "应用配置", "setting_plugin": "插件配置", + "simple_tool_tips": "该插件含有特殊输入,暂不支持被简易应用调用", "stream_response": "流输出", "stream_response_tip": "关闭该开关,可以强制模型使用非流模式,并且不会直接进行内容输出。可以在 AI 回复的输出中,获取本次模型输出的内容进行二次处理。", "temperature": "温度", @@ -135,6 +132,7 @@ "templateMarket.templateTags.Roleplay": "角色扮演", "templateMarket.templateTags.Web_search": "联网搜索", "templateMarket.templateTags.Writing": "文本创作", + "templateMarket.template_guide": "模板说明", "template_market": "模板市场", "template_market_description": "在模板市场探索更多玩法,配置教程与使用引导,带你理解并上手各种应用", "template_market_empty_data": "找不到合适的模板", @@ -155,7 +153,6 @@ "type.Plugin": "插件", "type.Simple bot": "简易应用", "type.Workflow bot": "工作流", - "unusual_leave_auto_save": "异常离开,自动保存", "upload_file_max_amount": "最大文件数量", "upload_file_max_amount_tip": "单轮对话中最大上传文件数量", "variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index f120ed2e7..6003b22ff 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -32,6 +32,11 @@ "Warning": "提示", "add_new": "新增", "add_new_param": "新增参数", + "app.templateMarket.templateTags.Image_generation": "图片生成", + "app.templateMarket.templateTags.Office_services": "办公服务", + "app.templateMarket.templateTags.Roleplay": "角色扮演", + "app.templateMarket.templateTags.Web_search": "联网搜索", + "app.templateMarket.templateTags.Writing": "文本创作", "back": "返回", "chose_condition": "选择条件", "chosen": "已选", @@ -682,6 +687,7 @@ "core.module.Variable": "全局变量", "core.module.Variable Setting": "变量设置", "core.module.edit.Field Name Cannot Be Empty": "字段名不能为空", + "core.module.edit.Field Value Type Cannot Be Empty": "可选数据类型不能为空", "core.module.extract.Add field": "新增字段", "core.module.extract.Enum Description": "列举出该字段可能的值,每行一个", "core.module.extract.Enum Value": "枚举值", @@ -895,6 +901,10 @@ "item_name": "字段名", "just_now": "刚刚", "key_repetition": "key 重复", + "max_quote_tokens": "引用上限", + "max_quote_tokens_tips": "单次搜索最大的 token 数量,中文约 1 字=1.7 tokens,英文约 1 字=1 token", + "min_similarity": "最低相关度", + "min_similarity_tip": "不同索引模型的相关度有区别,请通过搜索测试来选择合适的数值。使用 结果重排 时,使用重排结果进行过滤。", "model.billing": "模型计费", "model.model_type": "模型类型", "model.name": "模型名", diff --git a/packages/web/i18n/zh-Hant/account.json b/packages/web/i18n/zh-Hant/account.json index 82406e99a..395ebe1d2 100644 --- a/packages/web/i18n/zh-Hant/account.json +++ b/packages/web/i18n/zh-Hant/account.json @@ -9,5 +9,6 @@ "personalization": "個人化", "promotion_records": "促銷記錄", "team": "團隊管理", + "third_party": "第三方账号", "usage_records": "使用記錄" } diff --git a/packages/web/i18n/zh-Hant/account_info.json b/packages/web/i18n/zh-Hant/account_info.json index 8a7053137..a2792aaad 100644 --- a/packages/web/i18n/zh-Hant/account_info.json +++ b/packages/web/i18n/zh-Hant/account_info.json @@ -42,7 +42,6 @@ "notification_receiving": "通知接收", "notification_receiving_hint": "通知接收", "old_password": "舊密碼", - "open_api_notice": "可以填寫 OpenAI/OneAPI 的相關金鑰。\n如果你填寫了該內容,在線上平台使用【 AI 對話】、【問題分類】和【內容提取】將會走你填寫的 Key,不會計費用。\n請注意你的 Key 是否有存取對應模型的權限。 \nGPT 模型可以選擇 FastAI 。", "openai_account_configuration": "OpenAI 帳號配置", "openai_account_setting_exception": "設定 OpenAI 帳號異常", "package_and_usage": "套餐與用量", @@ -60,7 +59,6 @@ "please_bind_notification_receiving_path": "請先綁定通知接收途徑", "purchase_extra_package": "購買額外套餐", "reminder_create_bound_notification_account": "提醒創建者綁定通知帳號", - "request_address_notice": "請求地址,預設為 openai 官方。\n可填中轉位址,未自動補全 \"v1\"", "resource_usage": "資源用量", "select_avatar": "點選選擇頭像", "standard_package_and_extra_resource_package": "包含標準套餐與額外資源包", diff --git a/packages/web/i18n/zh-Hant/account_thirdParty.json b/packages/web/i18n/zh-Hant/account_thirdParty.json new file mode 100644 index 000000000..06d08c276 --- /dev/null +++ b/packages/web/i18n/zh-Hant/account_thirdParty.json @@ -0,0 +1,16 @@ +{ + "configured": "已配置", + "error.no_permission": "請聯繫管理員配置", + "laf_account": "af 帳號", + "no_intro": "暫無說明", + "not_configured": "未配置", + "open_api_notice": "可以填寫 OpenAI/OneAPI 的相關金鑰。\n如果你填寫了該內容,在線上平台使用【 AI 對話】、【問題分類】和【內容提取】將會走你填寫的 Key,不會計費用。\n請注意你的 Key 是否有存取對應模型的權限。 \nGPT 模型可以選擇 FastAI 。", + "openai_account_configuration": "OpenAI/OneAPI 帳號", + "request_address_notice": "請求地址,預設為 openai 官方。可填中轉位址,未自動補全 \"v1\"", + "third_party_account": "第三方帳號", + "third_party_account_desc": "管理員可以在這裡配置第三方帳號或變量,該帳號將被團隊所有人使用", + "unavailable": "取得使用量異常", + "usage": "使用量:", + "value_not_return_tip": "參數配置後,不會再次返回前端,無需洩漏給其他成員", + "value_placeholder": "輸入參數值。\n輸入空值表示刪除該配置。" +} diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index 99b2b489f..fa0bbd8c0 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -80,11 +80,7 @@ "mark_count": "標記答案數量", "max_histories_number": "記憶輪數", "max_histories_number_tip": "模型最多攜帶多少輪對話進入記憶中,如果記憶超出模型上下文,系統會強制截斷。\n所以儘管配置 30 輪對話,實際運行時候,不一定會達到 30 輪。", - "max_quote_tokens": "引用上限", - "max_quote_tokens_tips": "單次搜尋最大的 token 數量,中文約 1 字=1.7 tokens,英文約 1 字=1 token", "max_tokens": "回覆上限", - "min_similarity": "最低相關度", - "min_similarity_tip": "不同索引模型的相關度有區別,請透過搜尋測試來選擇合適的數值。\n使用 結果重排 時,使用重排結果過濾。", "module.Custom Title Tip": "這個標題會顯示在對話過程中", "module.No Modules": "找不到外掛", "module.type": "\"{{type}}\" 類型\n{{description}}", @@ -113,6 +109,7 @@ "search_app": "搜尋應用程式", "setting_app": "應用程式設定", "setting_plugin": "外掛設定", + "simple_tool_tips": "該插件含有特殊輸入,暫不支持被簡易應用調用", "stream_response": "流輸出", "stream_response_tip": "關閉該開關​​,可以強制模型使用非流模式,並且不會直接進行內容輸出。\n可在 AI 回覆的輸出中,取得本次模型輸出的內容進行二次處理。", "temperature": "溫度", @@ -135,6 +132,7 @@ "templateMarket.templateTags.Roleplay": "角色扮演", "templateMarket.templateTags.Web_search": "網路搜尋", "templateMarket.templateTags.Writing": "文字創作", + "templateMarket.template_guide": "模板說明", "template_market": "範本市集", "template_market_description": "在範本市集探索更多玩法,設定教學與使用指引,帶您理解並上手各種應用程式", "template_market_empty_data": "找不到合適的範本", @@ -155,7 +153,6 @@ "type.Plugin": "外掛", "type.Simple bot": "簡易應用程式", "type.Workflow bot": "工作流程", - "unusual_leave_auto_save": "異常離開,自動儲存", "upload_file_max_amount": "最大檔案數量", "upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量", "variable.select type_desc": "可以為工作流程定義全域變數,常用於暫存。賦值的方式包括:\n1. 從對話頁面的 query 參數取得。\n2. 透過 API 的 variables 物件傳遞。\n3. 透過【變數更新】節點進行賦值。", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index e9f692fc1..d92d30aef 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -32,6 +32,7 @@ "Warning": "警告", "add_new": "新增", "add_new_param": "新增參數", + "app.templateMarket.templateTags.Writing": "文字創作", "back": "返回", "chose_condition": "選擇條件", "chosen": "已選擇", @@ -683,6 +684,7 @@ "core.module.Variable": "全域變數", "core.module.Variable Setting": "變數設定", "core.module.edit.Field Name Cannot Be Empty": "欄位名稱不能為空", + "core.module.edit.Field Value Type Cannot Be Empty": "可選數據類型不能為空", "core.module.extract.Add field": "新增欄位", "core.module.extract.Enum Description": "列舉此欄位可能的值,每行一個", "core.module.extract.Enum Value": "列舉值", @@ -897,6 +899,10 @@ "item_name": "欄位名稱", "just_now": "剛剛", "key_repetition": "鍵值重複", + "max_quote_tokens": "引用上限", + "max_quote_tokens_tips": "單次搜尋最大的 token 數量,中文約 1 字=1.7 tokens,英文約 1 字=1 token", + "min_similarity": "最低相關度", + "min_similarity_tip": "不同索引模型的相關度有區別,請透過搜尋測試來選擇合適的數值。\n使用 結果重排 時,使用重排結果過濾。", "model.billing": "模型計費", "model.model_type": "模型類型", "model.name": "模型名", diff --git a/packages/web/styles/theme.ts b/packages/web/styles/theme.ts index 0114a7db8..bef9a30f6 100644 --- a/packages/web/styles/theme.ts +++ b/packages/web/styles/theme.ts @@ -240,7 +240,7 @@ const Button = defineStyleConfig({ }, grayDanger: { bg: 'myGray.150', - color: 'myGray.900', + color: 'myGray.600', _hover: { color: 'red.600', background: 'red.1', diff --git a/packages/web/types/i18next.d.ts b/packages/web/types/i18next.d.ts index a13ce1c1b..13fa5c442 100644 --- a/packages/web/types/i18next.d.ts +++ b/packages/web/types/i18next.d.ts @@ -1,6 +1,7 @@ import 'i18next'; import account_team from '../i18n/zh-CN/account_team.json'; import account from '../i18n/zh-CN/account.json'; +import account_thirdParty from '../i18n/zh-CN/account_thirdParty.json'; import account_promotion from '../i18n/zh-CN/account_promotion.json'; import account_inform from '../i18n/zh-CN/account_inform.json'; import account_setting from '../i18n/zh-CN/account_setting.json'; @@ -37,6 +38,7 @@ export interface I18nNamespaces { account_promotion: typeof account_promotion; account: typeof account; account_team: typeof account_team; + account_thirdParty: typeof account_thirdParty; } export type I18nNsType = (keyof I18nNamespaces)[]; @@ -69,6 +71,7 @@ declare module 'i18next' { 'account_setting', 'account_inform', 'account_promotion', + 'account_thirdParty', 'account', 'account_team' ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85183f18c..37ee67403 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,6 +87,9 @@ importers: axios: specifier: ^1.5.1 version: 1.7.2 + cheerio: + specifier: 1.0.0-rc.12 + version: 1.0.0-rc.12 duck-duck-scrape: specifier: ^2.2.5 version: 2.2.5 @@ -267,6 +270,15 @@ importers: specifier: ^5.0.4 version: 5.0.5 + packages/templates: + devDependencies: + '@fastgpt/global': + specifier: workspace:* + version: link:../global + '@fastgpt/service': + specifier: workspace:* + version: link:../service + packages/web: dependencies: '@chakra-ui/anatomy': @@ -426,6 +438,9 @@ importers: '@fastgpt/service': specifier: workspace:* version: link:../../packages/service + '@fastgpt/templates': + specifier: workspace:* + version: link:../../packages/templates '@fastgpt/web': specifier: workspace:* version: link:../../packages/web @@ -14504,7 +14519,7 @@ snapshots: eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0) eslint-plugin-react: 7.34.4(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0) @@ -14528,7 +14543,7 @@ snapshots: enhanced-resolve: 5.17.0 eslint: 8.56.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.14.0 @@ -14550,7 +14565,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 diff --git a/projects/app/.env.template b/projects/app/.env.template index 9e24af247..b8f4ca0a8 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -16,7 +16,7 @@ OPENAI_BASE_URL=https://api.openai.com/v1 # 通用key。可以是 openai 的也可以是 oneapi 的。 # 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key CHAT_API_KEY=sk-xxxx -# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true +# 强制将图片转成 base64 传递给模型 MULTIPLE_DATA_TO_BASE64=true # mongo 数据库连接参数,本地开发连接远程数据库时,可能需要增加 directConnection=true 参数,才能连接上。 @@ -26,8 +26,8 @@ MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin # PG 向量库连接参数 PG_URL=postgresql://username:password@host:port/postgres # milvus 向量库连接参数 -MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com -MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d +MILVUS_ADDRESS= +MILVUS_TOKEN= # code sandbox url SANDBOX_URL=http://localhost:3001 diff --git a/projects/app/package.json b/projects/app/package.json index dde631033..59beab69b 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.8.16", + "version": "4.8.17", "private": false, "scripts": { "dev": "next dev", @@ -23,6 +23,7 @@ "@fastgpt/plugins": "workspace:*", "@fastgpt/service": "workspace:*", "@fastgpt/web": "workspace:*", + "@fastgpt/templates": "workspace:*", "@fortaine/fetch-event-source": "^3.0.6", "@node-rs/jieba": "1.10.0", "@tanstack/react-query": "^4.24.10", diff --git a/projects/app/src/components/Layout/auth.tsx b/projects/app/src/components/Layout/auth.tsx index 52d6b5225..0c2c0c42b 100644 --- a/projects/app/src/components/Layout/auth.tsx +++ b/projects/app/src/components/Layout/auth.tsx @@ -17,7 +17,7 @@ const unAuthPage: { [key: string]: boolean } = { '/price': true }; -const Auth = ({ children }: { children: JSX.Element }) => { +const Auth = ({ children }: { children: JSX.Element | React.ReactNode }) => { const { t } = useTranslation(); const router = useRouter(); const { toast } = useToast(); diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index b48a9fd8d..2cebee18e 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -80,14 +80,14 @@ const Layout = ({ children }: { children: JSX.Element }) => { {isHideNavbar ? ( {children} ) : ( - <> + - {children} + {children} - + )} )} @@ -96,14 +96,16 @@ const Layout = ({ children }: { children: JSX.Element }) => { {phoneUnShowLayoutRoute[router.pathname] || isChatPage ? ( {children} ) : ( - - - {children} - - - - - + + + + {children} + + + + + + )} )} diff --git a/projects/app/src/components/Layout/navbar.tsx b/projects/app/src/components/Layout/navbar.tsx index ad714621e..e9b15acba 100644 --- a/projects/app/src/components/Layout/navbar.tsx +++ b/projects/app/src/components/Layout/navbar.tsx @@ -82,6 +82,7 @@ const Navbar = ({ unread }: { unread: number }) => { '/account/info', '/account/team', '/account/usage', + '/account/thirdParty', '/account/apikey', '/account/setting', '/account/inform', diff --git a/projects/app/src/components/Markdown/index.tsx b/projects/app/src/components/Markdown/index.tsx index 671c15d3f..4aed17b97 100644 --- a/projects/app/src/components/Markdown/index.tsx +++ b/projects/app/src/components/Markdown/index.tsx @@ -28,17 +28,22 @@ const IframeHtmlCodeBlock = dynamic(() => import('./codeBlock/iframe-html'), { s const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false }); const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false }); -const Markdown = ({ - source = '', - showAnimation = false, - isDisabled = false, - forbidZhFormat = false -}: { +type Props = { source?: string; showAnimation?: boolean; isDisabled?: boolean; forbidZhFormat?: boolean; -}) => { +}; +const Markdown = (props: Props) => { + const source = props.source || ''; + + if (source.length < 200000) { + return ; + } + + return {source}; +}; +const MarkdownRender = ({ source = '', showAnimation, isDisabled, forbidZhFormat }: Props) => { const components = useMemo( () => ({ img: Image, diff --git a/projects/app/src/components/Select/AIModelSelector.tsx b/projects/app/src/components/Select/AIModelSelector.tsx index b7e146fff..cb90b4b0f 100644 --- a/projects/app/src/components/Select/AIModelSelector.tsx +++ b/projects/app/src/components/Select/AIModelSelector.tsx @@ -24,12 +24,6 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => { const { t } = useTranslation(); const { feConfigs, llmModelList, vectorModelList } = useSystemStore(); - const { - isOpen: isOpenAiPointsModal, - onClose: onCloseAiPointsModal, - onOpen: onOpenAiPointsModal - } = useDisclosure(); - const avatarSize = useMemo(() => { const size = { sm: '1rem', @@ -74,17 +68,6 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => { : avatarList; }, [feConfigs.show_pay, avatarList, avatarSize, t]); - const onSelect = useCallback( - (e: string) => { - if (e === 'price') { - onOpenAiPointsModal(); - return; - } - return onchange?.(e); - }, - [onOpenAiPointsModal, onchange] - ); - return ( { }} > - + + {({ onOpen }) => ( + { + if (e === 'price') { + onOpen(); + return; + } + return onchange?.(e); + }} + /> + )} + - - {isOpenAiPointsModal && } ); }; const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) => { const { t } = useTranslation(); - const { feConfigs, llmModelList, vectorModelList } = useSystemStore(); + const { llmModelList, vectorModelList } = useSystemStore(); const [value, setValue] = useState([]); - const { - isOpen: isOpenAiPointsModal, - onClose: onCloseAiPointsModal, - onOpen: onOpenAiPointsModal - } = useDisclosure(); - const avatarSize = useMemo(() => { const size = { sm: '1rem', @@ -211,8 +196,6 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) => }} /> - - {isOpenAiPointsModal && } ); }; diff --git a/projects/app/src/components/common/Modal/UseGuideModal.tsx b/projects/app/src/components/common/Modal/UseGuideModal.tsx new file mode 100644 index 000000000..09f5a8cfd --- /dev/null +++ b/projects/app/src/components/common/Modal/UseGuideModal.tsx @@ -0,0 +1,46 @@ +import { Box, ModalBody, useDisclosure } from '@chakra-ui/react'; +import Markdown from '@/components/Markdown'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import { getDocPath } from '@/web/common/system/doc'; +import React from 'react'; + +const UseGuideModal = ({ + children, + title, + iconSrc, + text, + link +}: { + children: ({ onClick }: { onClick: () => void }) => React.ReactNode; + title?: string; + iconSrc?: string; + text?: string; + link?: string; +}) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + const onClick = () => { + if (link) { + return window.open(getDocPath(link), '_blank'); + } + if (text) { + return onOpen(); + } + }; + + return ( + <> + {children({ onClick })} + {isOpen && ( + + + + + + + + )} + + ); +}; + +export default React.memo(UseGuideModal); diff --git a/projects/app/src/components/core/ai/AISettingModal/index.tsx b/projects/app/src/components/core/ai/AISettingModal/index.tsx index f36ac74b9..2d921ecdd 100644 --- a/projects/app/src/components/core/ai/AISettingModal/index.tsx +++ b/projects/app/src/components/core/ai/AISettingModal/index.tsx @@ -100,12 +100,6 @@ const AIChatSettingsModal = ({ setRefresh(!refresh); }; - const { - isOpen: isOpenAiPointsModal, - onClose: onCloseAiPointsModal, - onOpen: onOpenAiPointsModal - } = useDisclosure(); - return ( {t('app:ai_point_price')} - + + {({ onOpen }) => ( + + )} + @@ -327,8 +322,6 @@ const AIChatSettingsModal = ({ {t('common:common.Confirm')} - - {isOpenAiPointsModal && } ); }; diff --git a/projects/app/src/components/core/ai/ModelTable/index.tsx b/projects/app/src/components/core/ai/ModelTable/index.tsx index 17a80675b..4ef39dc14 100644 --- a/projects/app/src/components/core/ai/ModelTable/index.tsx +++ b/projects/app/src/components/core/ai/ModelTable/index.tsx @@ -9,9 +9,9 @@ import { Td, Th, Thead, - Tr + Tr, + useDisclosure } from '@chakra-ui/react'; -import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import React, { useMemo, useRef, useState } from 'react'; import { @@ -26,6 +26,9 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyTag from '@fastgpt/web/components/common/Tag/index'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; +import dynamic from 'next/dynamic'; + +const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal')); const ModelTable = () => { const { t } = useTranslation(); @@ -156,6 +159,19 @@ const ModelTable = () => { search ]); + const filterProviderList = useMemo(() => { + const allProviderIds: string[] = [ + ...llmModelList, + ...vectorModelList, + ...audioSpeechModelList, + whisperModel + ].map((model) => model.provider); + + return providerList.current.filter( + (item) => allProviderIds.includes(item.value) || item.value === '' + ); + }, [audioSpeechModelList, llmModelList, vectorModelList, whisperModel]); + return ( @@ -168,7 +184,7 @@ const ModelTable = () => { bg={'myGray.50'} value={provider} onchange={setProvider} - list={providerList.current} + list={filterProviderList} /> @@ -228,24 +244,34 @@ const ModelTable = () => { export default ModelTable; -export const ModelPriceModal = ({ onClose }: { onClose: () => void }) => { +export const ModelPriceModal = ({ + children +}: { + children: ({ onOpen }: { onOpen: () => void }) => React.ReactNode; +}) => { const { t } = useTranslation(); + const { isOpen, onOpen, onClose } = useDisclosure(); return ( - - - - - + <> + {children({ onOpen })} + {isOpen && ( + + + + + + )} + ); }; diff --git a/projects/app/src/components/core/app/DatasetParamsModal.tsx b/projects/app/src/components/core/app/DatasetParamsModal.tsx index ad575ef75..b99563308 100644 --- a/projects/app/src/components/core/app/DatasetParamsModal.tsx +++ b/projects/app/src/components/core/app/DatasetParamsModal.tsx @@ -226,8 +226,8 @@ const DatasetParamsModal = ({ {limit !== undefined && ( - {t('app:max_quote_tokens')} - + {t('common:max_quote_tokens')} + - {t('app:min_similarity')} - + {t('common:min_similarity')} + {showSimilarity ? ( diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx index 23b45caa0..89879915d 100644 --- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Flex, Switch, Textarea } from '@chakra-ui/react'; +import { Box, Button, Flex, Switch, Textarea, useDisclosure } from '@chakra-ui/react'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; @@ -20,6 +20,8 @@ import { isEqual } from 'lodash'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext'; import { PluginRunContext } from '../context'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import AIModelSelector from '@/components/Select/AIModelSelector'; const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor')); @@ -152,6 +154,7 @@ const RenderPluginInput = ({ }) => { const { t } = useTranslation(); const inputType = input.renderTypeList[0]; + const { llmModelList } = useSystemStore(); const render = (() => { if (inputType === FlowNodeInputTypeEnum.customVariable) { @@ -167,7 +170,19 @@ const RenderPluginInput = ({ ); } - + if (inputType === FlowNodeInputTypeEnum.selectLLMModel) { + return ( + ({ + value: item.model, + label: item.name + }))} + onchange={onChange} + /> + ); + } if (input.valueType === WorkflowIOValueTypeEnum.string) { return (