From 443ad37b6a2631ed51367a5c2231796987dea7bc Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Fri, 23 Feb 2024 17:47:34 +0800 Subject: [PATCH] sub plan page (#885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: insert mongo dataset data session * perf: dataset data index * remove delay * rename bill schema * rename bill record * perf: bill table * perf: prompt * perf: sub plan * change the usage count * feat: usage bill * publish usages * doc * 新增团队聊天功能 (#20) * perf: doc * feat 添加标签部分 feat 信息团队标签配置 feat 新增团队同步管理 feat team分享页面 feat 完成team分享页面 feat 实现模糊搜索 style 格式化 fix 修复迷糊匹配 style 样式修改 fix 团队标签功能修复 * fix 修复鉴权功能 * merge 合并代码 * fix 修复引用错误 * fix 修复pr问题 * fix 修复ts格式问题 --------- Co-authored-by: archer <545436317@qq.com> Co-authored-by: liuxingwan * update extra plan * fix: ts * format * perf: bill field * feat: standard plan * fix: ts * feat 个人账号页面修改 (#22) * feat 添加标签部分 feat 信息团队标签配置 feat 新增团队同步管理 feat team分享页面 feat 完成team分享页面 feat 实现模糊搜索 style 格式化 fix 修复迷糊匹配 style 样式修改 fix 团队标签功能修复 * fix 修复鉴权功能 * merge 合并代码 * fix 修复引用错误 * fix 修复pr问题 * fix 修复ts格式问题 * feat 修改个人账号页 --------- Co-authored-by: liuxingwan * fix chunk index; error page text * feat: dataset process Integral prediction * feat: stand plan field * feat: sub plan limit * perf: index * query extension * perf: share link push app name * perf: plan point unit * perf: get sub plan * perf: account page --------- Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com> Co-authored-by: liuxingwan --- docSite/content/docs/course/data_search.md | 8 +- .../content/docs/development/configuration.md | 185 +----- docSite/content/docs/development/docker.md | 1 + docSite/content/docs/development/one-api.md | 3 +- .../docs/development/openapi/dataset.md | 78 ++- .../content/docs/development/openapi/share.md | 137 ++-- .../content/docs/development/upgrading/465.md | 4 +- .../content/docs/development/upgrading/467.md | 2 +- .../content/docs/development/upgrading/468.md | 1 + .../content/docs/development/upgrading/469.md | 13 + .../docs/workflow/examples/google_search.md | 6 +- .../docs/workflow/examples/lab_appointment.md | 8 +- .../workflow/modules/coreferenceResolution.md | 6 +- package.json | 2 +- packages/global/common/error/code/team.ts | 18 +- packages/global/common/math/tools.ts | 4 + packages/global/core/ai/model.d.ts | 15 +- packages/global/core/ai/model.ts | 6 +- packages/global/core/app/api.d.ts | 1 + packages/global/core/app/type.d.ts | 3 +- packages/global/core/chat/constants.ts | 6 +- packages/global/core/chat/type.d.ts | 28 +- packages/global/core/dataset/constants.ts | 24 - packages/global/core/dataset/type.d.ts | 5 +- packages/global/core/dataset/utils.ts | 3 +- packages/global/core/module/constants.ts | 3 +- packages/global/core/module/node/constant.ts | 6 +- .../core/module/template/system/aiChat.ts | 2 +- .../template/system/classifyQuestion.ts | 2 +- .../module/template/system/contextExtract.ts | 2 +- ...ferenceResolution.ts => queryExtension.ts} | 15 +- packages/global/core/module/type.d.ts | 19 +- packages/global/support/openapi/type.d.ts | 5 +- packages/global/support/outLink/api.d.ts | 2 +- packages/global/support/outLink/type.d.ts | 4 +- packages/global/support/permission/type.d.ts | 1 - packages/global/support/user/team/constant.ts | 1 + .../global/support/user/team/controller.d.ts | 1 + packages/global/support/user/team/type.d.ts | 18 +- packages/global/support/user/type.d.ts | 1 + packages/global/support/wallet/bill/api.d.ts | 37 +- .../global/support/wallet/bill/constants.ts | 75 ++- packages/global/support/wallet/bill/tools.ts | 26 - packages/global/support/wallet/bill/type.d.ts | 54 +- packages/global/support/wallet/constants.ts | 3 + .../global/support/wallet/pay/constants.ts | 41 -- packages/global/support/wallet/pay/type.d.ts | 18 - packages/global/support/wallet/sub/api.d.ts | 14 +- .../global/support/wallet/sub/constants.ts | 16 +- packages/global/support/wallet/sub/type.d.ts | 43 +- packages/global/support/wallet/usage/api.d.ts | 26 + .../global/support/wallet/usage/constants.ts | 21 + packages/global/support/wallet/usage/tools.ts | 23 + .../global/support/wallet/usage/type.d.ts | 26 + .../service/common/vectorStore/controller.ts | 34 +- .../common/vectorStore/pg/controller.ts | 12 + packages/service/core/ai/config.ts | 1 + packages/service/core/ai/functions/cfr.ts | 159 ----- .../core/ai/functions/createQuestionGuide.ts | 29 +- .../core/ai/functions/queryExtension.ts | 45 +- packages/service/core/app/schema.ts | 3 + packages/service/core/chat/chatItemSchema.ts | 2 + packages/service/core/chat/chatSchema.ts | 3 + packages/service/core/chat/utils.ts | 11 +- .../core/dataset/collection/controller.ts | 2 - packages/service/core/dataset/controller.ts | 5 + packages/service/core/dataset/data/schema.ts | 9 - packages/service/core/dataset/search/utils.ts | 72 ++- .../core/dataset/training/controller.ts | 4 +- .../service/core/dataset/training/schema.ts | 7 +- packages/service/package.json | 2 +- packages/service/support/openapi/auth.ts | 5 +- packages/service/support/openapi/schema.ts | 19 +- packages/service/support/openapi/tools.ts | 12 +- packages/service/support/outLink/schema.ts | 17 +- packages/service/support/outLink/tools.ts | 14 +- .../support/permission/auth/dataset.ts | 8 +- .../service/support/permission/auth/user.ts | 3 +- .../service/support/permission/controller.ts | 6 +- .../support/permission/limit/dataset.ts | 23 - packages/service/support/user/controller.ts | 20 - packages/service/support/user/schema.ts | 4 +- .../service/support/user/team/controller.ts | 2 +- .../service/support/user/team/teamSchema.ts | 5 +- .../support/user/team/teamTagsSchema.ts | 35 + packages/service/support/wallet/sub/schema.ts | 117 +--- packages/service/support/wallet/sub/utils.ts | 101 ++- .../wallet/{bill => usage}/controller.ts | 18 +- .../support/wallet/{bill => usage}/schema.ts | 39 +- .../web/components/common/Icon/constants.ts | 19 +- .../common/Icon/icons/acount/arrowRight.svg | 3 + .../common/Icon/icons/acount/check.svg | 3 + .../common/Icon/icons/acount/cube.svg | 3 + .../common/Icon/icons/acount/personalized.svg | 3 + .../common/Icon/icons/acount/plans.svg | 5 + .../common/Icon/icons/acount/plansBlue.svg | 5 + .../common/Icon/icons/acount/user.svg | 3 + .../common/Icon/icons/modal/confirmPay.svg | 5 + .../{pay => bill}/extraDatasetsize.svg | 0 .../support/{pay => bill}/extraPoints.svg | 0 .../support/{pay => bill}/payRecordLight.svg | 0 .../support/{pay => bill}/priceLight.svg | 0 .../Icon/icons/support/bill/shoppingCart.svg | 4 + .../usageRecordLight.svg} | 0 pnpm-lock.yaml | 20 +- projects/app/data/config.json | 59 +- projects/app/next.config.js | 8 +- projects/app/package.json | 4 +- projects/app/public/docs/versionIntro.md | 2 +- projects/app/public/locales/en/common.json | 134 ++-- projects/app/public/locales/zh/common.json | 167 +++-- .../components/ChatBox/WholeResponseModal.tsx | 18 +- projects/app/src/components/Layout/auth.tsx | 1 + projects/app/src/components/Layout/index.tsx | 5 +- .../app/src/components/Markdown/index.tsx | 2 +- .../src/components/Select/SelectAiModel.tsx | 17 +- projects/app/src/components/TagEdit/index.tsx | 103 +++ .../core/module/DatasetParamsModal.tsx | 20 +- .../components/render/RenderInput/index.tsx | 6 +- .../src/components/core/module/Flow/index.tsx | 2 +- .../src/components/support/apikey/Table.tsx | 18 +- .../user/team/TeamManageModal/index.tsx | 68 +- .../support/user/team/TeamTagsAsync/index.tsx | 182 ++++++ .../src/components/support/wallet/Price.tsx | 97 --- .../support/wallet/QRCodePayModal.tsx | 84 +++ .../wallet/StandardPlanContentList.tsx | 116 ++++ .../support/wallet/SubDatasetModal.tsx | 240 ------- projects/app/src/constants/app.ts | 5 +- projects/app/src/global/core/chat/api.d.ts | 7 + projects/app/src/global/core/prompt/AIChat.ts | 54 +- projects/app/src/pages/_error.tsx | 12 +- .../pages/account/components/BillDetail.tsx | 160 ----- .../pages/account/components/BillTable.tsx | 289 +++++---- .../app/src/pages/account/components/Info.tsx | 609 +++++++++++------- .../src/pages/account/components/InfoOld.tsx | 439 +++++++++++++ .../src/pages/account/components/PayModal.tsx | 144 ++--- .../account/components/PayRecordTable.tsx | 108 ---- .../pages/account/components/UsageDetail.tsx | 119 ++++ .../pages/account/components/UsageTable.tsx | 189 ++++++ .../components/standardDetailModal.tsx | 25 + projects/app/src/pages/account/index.tsx | 38 +- projects/app/src/pages/api/admin/initv468.ts | 99 +++ projects/app/src/pages/api/admin/initv469.ts | 35 + .../app/src/pages/api/common/file/upload.ts | 2 +- .../api/core/ai/agent/createQuestionGuide.ts | 9 +- projects/app/src/pages/api/core/app/create.ts | 8 +- .../src/pages/api/core/app/data/totalUsage.ts | 51 -- .../core/app/form2Modules/fastgpt-simple.ts | 4 +- .../app/form2Modules/fastgpt-universal.ts | 4 +- projects/app/src/pages/api/core/app/update.ts | 3 +- .../src/pages/api/core/app/updateTeamTasg.ts | 82 +++ .../app/src/pages/api/core/chat/chatTest.ts | 19 +- .../src/pages/api/core/chat/clearHistories.ts | 9 +- .../src/pages/api/core/chat/getHistories.ts | 8 + .../src/pages/api/core/chat/item/getSpeech.ts | 8 +- .../api/core/chat/outLink/getInforByTeamId.ts | 37 ++ .../app/src/pages/api/core/chat/team/init.ts | 91 +++ .../app/src/pages/api/core/chat/teamInit.ts | 81 +++ .../src/pages/api/core/chat/updateHistory.ts | 1 + .../core/dataset/collection/create/link.ts | 14 +- .../core/dataset/collection/create/text.ts | 14 +- .../api/core/dataset/collection/sync/link.ts | 8 +- .../app/src/pages/api/core/dataset/create.ts | 9 +- .../src/pages/api/core/dataset/data/delete.ts | 17 +- .../pages/api/core/dataset/data/insertData.ts | 13 +- .../pages/api/core/dataset/data/pushData.ts | 6 +- .../src/pages/api/core/dataset/data/update.ts | 11 +- .../src/pages/api/core/dataset/searchTest.ts | 23 +- .../app/src/pages/api/core/plugin/create.ts | 3 + .../src/pages/api/plugins/textEditor/index.ts | 1 - .../pages/api/support/user/account/update.ts | 9 +- .../user/account/updatePasswordByOld.ts | 9 +- .../user/team/limit/datasetSizeLimit.ts | 6 +- .../support/wallet/sub/getTeamSubStatus.ts | 9 +- .../createTrainingUsage.ts} | 12 +- .../src/pages/api/v1/audio/transcriptions.ts | 4 +- .../app/src/pages/api/v1/chat/completions.ts | 190 +++--- projects/app/src/pages/api/v1/embeddings.ts | 14 +- projects/app/src/pages/api/v1/rerank.ts | 50 -- .../detail/components/Charts/TotalUsage.tsx | 201 ------ .../app/detail/components/OutLink/Share.tsx | 17 +- .../detail/components/SimpleEdit/AppCard.tsx | 18 +- .../components/SimpleEdit/tagsEditModal.tsx | 103 +++ .../pages/app/list/component/CreateModal.tsx | 6 +- projects/app/src/pages/app/list/index.tsx | 16 +- projects/app/src/pages/chat/team.tsx | 521 +++++++++++++++ .../detail/components/Import/Provider.tsx | 31 +- .../Import/commonProgress/DataProcess.tsx | 4 +- .../Import/commonProgress/Upload.tsx | 4 +- .../detail/components/InputDataModal.tsx | 86 +-- .../app/src/pages/dataset/detail/index.tsx | 5 +- .../src/pages/price/components/ExtraPlan.tsx | 460 +++++++------ .../app/src/pages/price/components/FAQ.tsx | 68 +- .../app/src/pages/price/components/Points.tsx | 10 +- .../src/pages/price/components/Standard.tsx | 393 ++++++----- projects/app/src/pages/price/index.tsx | 52 +- projects/app/src/pages/tools/index.tsx | 9 + projects/app/src/pages/tools/price.tsx | 17 - .../service/core/dataset/data/controller.ts | 169 ++--- projects/app/src/service/events/generateQA.ts | 18 +- .../app/src/service/events/generateVector.ts | 14 +- .../moduleDispatch/agent/classifyQuestion.ts | 51 +- .../service/moduleDispatch/agent/extract.ts | 57 +- .../src/service/moduleDispatch/chat/oneapi.ts | 74 +-- .../service/moduleDispatch/dataset/search.ts | 60 +- .../app/src/service/moduleDispatch/index.ts | 25 +- .../src/service/moduleDispatch/plugin/run.ts | 24 +- .../moduleDispatch/plugin/runOutput.ts | 2 +- .../src/service/moduleDispatch/tools/cfr.ts | 64 -- .../src/service/moduleDispatch/tools/http.ts | 20 +- .../service/moduleDispatch/tools/http468.ts | 21 +- .../moduleDispatch/tools/queryExternsion.ts | 77 +++ .../service/moduleDispatch/tools/runApp.ts | 28 +- projects/app/src/service/mongo.ts | 2 +- .../service/support/permission/auth/bill.ts | 9 - .../support/permission/auth/dataset.ts | 1 + .../support/permission/auth/outLink.ts | 10 +- .../service/support/permission/auth/team.ts | 18 + .../support/permission/auth/teamChat.ts | 44 ++ .../service/support/permission/teamLimit.ts | 102 +++ .../service/support/wallet/bill/controller.ts | 23 - .../src/service/support/wallet/bill/push.ts | 327 ---------- .../src/service/support/wallet/bill/utils.ts | 54 -- .../support/wallet/usage/controller.ts | 23 + .../src/service/support/wallet/usage/push.ts | 274 ++++++++ .../src/service/support/wallet/usage/utils.ts | 44 ++ .../app/src/service/utils/chat/saveChat.ts | 82 ++- projects/app/src/types/i18n.d.ts | 2 +- projects/app/src/types/user.d.ts | 3 +- projects/app/src/web/common/api/fetch.ts | 26 +- projects/app/src/web/core/app/api.ts | 6 + .../app/src/web/core/app/store/useAppStore.ts | 12 +- projects/app/src/web/core/app/templates.ts | 10 +- projects/app/src/web/core/chat/api.ts | 21 +- .../app/src/web/core/chat/storeTeamChat.ts | 42 ++ .../app/src/web/core/dataset/store/dataset.ts | 20 +- projects/app/src/web/core/dataset/utils.ts | 6 - .../src/web/core/modules/template/system.ts | 10 +- projects/app/src/web/styles/theme.ts | 25 +- projects/app/src/web/support/user/team/api.ts | 9 + .../app/src/web/support/user/useUserStore.ts | 29 +- .../app/src/web/support/wallet/bill/api.ts | 26 +- .../app/src/web/support/wallet/pay/api.ts | 17 - .../app/src/web/support/wallet/sub/api.ts | 18 +- .../src/web/support/wallet/sub/constants.ts | 2 + .../app/src/web/support/wallet/usage/api.ts | 10 + 246 files changed, 6277 insertions(+), 4272 deletions(-) create mode 100644 docSite/content/docs/development/upgrading/469.md create mode 100644 packages/global/common/math/tools.ts rename packages/global/core/module/template/system/{coreferenceResolution.ts => queryExtension.ts} (76%) delete mode 100644 packages/global/support/wallet/bill/tools.ts create mode 100644 packages/global/support/wallet/constants.ts delete mode 100644 packages/global/support/wallet/pay/constants.ts delete mode 100644 packages/global/support/wallet/pay/type.d.ts create mode 100644 packages/global/support/wallet/usage/api.d.ts create mode 100644 packages/global/support/wallet/usage/constants.ts create mode 100644 packages/global/support/wallet/usage/tools.ts create mode 100644 packages/global/support/wallet/usage/type.d.ts delete mode 100644 packages/service/core/ai/functions/cfr.ts delete mode 100644 packages/service/support/permission/limit/dataset.ts create mode 100644 packages/service/support/user/team/teamTagsSchema.ts rename packages/service/support/wallet/{bill => usage}/controller.ts (62%) rename packages/service/support/wallet/{bill => usage}/schema.ts (50%) create mode 100644 packages/web/components/common/Icon/icons/acount/arrowRight.svg create mode 100644 packages/web/components/common/Icon/icons/acount/check.svg create mode 100644 packages/web/components/common/Icon/icons/acount/cube.svg create mode 100644 packages/web/components/common/Icon/icons/acount/personalized.svg create mode 100644 packages/web/components/common/Icon/icons/acount/plans.svg create mode 100644 packages/web/components/common/Icon/icons/acount/plansBlue.svg create mode 100644 packages/web/components/common/Icon/icons/acount/user.svg create mode 100644 packages/web/components/common/Icon/icons/modal/confirmPay.svg rename packages/web/components/common/Icon/icons/support/{pay => bill}/extraDatasetsize.svg (100%) rename packages/web/components/common/Icon/icons/support/{pay => bill}/extraPoints.svg (100%) rename packages/web/components/common/Icon/icons/support/{pay => bill}/payRecordLight.svg (100%) rename packages/web/components/common/Icon/icons/support/{pay => bill}/priceLight.svg (100%) create mode 100644 packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg rename packages/web/components/common/Icon/icons/support/{bill/billRecordLight.svg => usage/usageRecordLight.svg} (100%) create mode 100644 projects/app/src/components/TagEdit/index.tsx create mode 100644 projects/app/src/components/support/user/team/TeamTagsAsync/index.tsx delete mode 100644 projects/app/src/components/support/wallet/Price.tsx create mode 100644 projects/app/src/components/support/wallet/QRCodePayModal.tsx create mode 100644 projects/app/src/components/support/wallet/StandardPlanContentList.tsx delete mode 100644 projects/app/src/components/support/wallet/SubDatasetModal.tsx delete mode 100644 projects/app/src/pages/account/components/BillDetail.tsx create mode 100644 projects/app/src/pages/account/components/InfoOld.tsx delete mode 100644 projects/app/src/pages/account/components/PayRecordTable.tsx create mode 100644 projects/app/src/pages/account/components/UsageDetail.tsx create mode 100644 projects/app/src/pages/account/components/UsageTable.tsx create mode 100644 projects/app/src/pages/account/components/standardDetailModal.tsx create mode 100644 projects/app/src/pages/api/admin/initv468.ts create mode 100644 projects/app/src/pages/api/admin/initv469.ts delete mode 100644 projects/app/src/pages/api/core/app/data/totalUsage.ts create mode 100644 projects/app/src/pages/api/core/app/updateTeamTasg.ts create mode 100644 projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts create mode 100644 projects/app/src/pages/api/core/chat/team/init.ts create mode 100644 projects/app/src/pages/api/core/chat/teamInit.ts rename projects/app/src/pages/api/support/wallet/{bill/createTrainingBill.ts => usage/createTrainingUsage.ts} (68%) delete mode 100644 projects/app/src/pages/api/v1/rerank.ts delete mode 100644 projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx create mode 100644 projects/app/src/pages/app/detail/components/SimpleEdit/tagsEditModal.tsx create mode 100644 projects/app/src/pages/chat/team.tsx delete mode 100644 projects/app/src/pages/tools/price.tsx delete mode 100644 projects/app/src/service/moduleDispatch/tools/cfr.ts create mode 100644 projects/app/src/service/moduleDispatch/tools/queryExternsion.ts delete mode 100644 projects/app/src/service/support/permission/auth/bill.ts create mode 100644 projects/app/src/service/support/permission/auth/team.ts create mode 100644 projects/app/src/service/support/permission/auth/teamChat.ts create mode 100644 projects/app/src/service/support/permission/teamLimit.ts delete mode 100644 projects/app/src/service/support/wallet/bill/controller.ts delete mode 100644 projects/app/src/service/support/wallet/bill/push.ts delete mode 100644 projects/app/src/service/support/wallet/bill/utils.ts create mode 100644 projects/app/src/service/support/wallet/usage/controller.ts create mode 100644 projects/app/src/service/support/wallet/usage/push.ts create mode 100644 projects/app/src/service/support/wallet/usage/utils.ts create mode 100644 projects/app/src/web/core/chat/storeTeamChat.ts delete mode 100644 projects/app/src/web/support/wallet/pay/api.ts create mode 100644 projects/app/src/web/support/wallet/sub/constants.ts create mode 100644 projects/app/src/web/support/wallet/usage/api.ts diff --git a/docSite/content/docs/course/data_search.md b/docSite/content/docs/course/data_search.md index 64fcc5bd2..baa74cd13 100644 --- a/docSite/content/docs/course/data_search.md +++ b/docSite/content/docs/course/data_search.md @@ -56,7 +56,7 @@ FastGPT 采用了`PostgresSQL`的`PG Vector`插件作为向量检索器,索引 ### 检索方案 -1. 通过`问题补全`实现指代消除和问题扩展,从而增加连续对话的检索能力以及语义丰富度。 +1. 通过`问题优化`实现指代消除和问题扩展,从而增加连续对话的检索能力以及语义丰富度。 2. 通过`Concat query`来增加`Rerank`连续对话的时,排序的准确性。 3. 通过`RRF`合并方式,综合多个渠道的检索效果。 4. 通过`Rerank`来二次排序,提高精度。 @@ -97,7 +97,7 @@ FastGPT 采用了`PostgresSQL`的`PG Vector`插件作为向量检索器,索引 #### 结果重排 -利用`ReRank`模型对搜索结果进行重排,绝大多数情况下,可以有效提高搜索结果的准确率。不过,重排模型与问题的完整度(主谓语齐全)有一些关系,通常会先走问题补全后再进行搜索-重排。重排后可以得到一个`0-1`的得分,代表着搜索内容与问题的相关度,该分数通常比向量的得分更加精确,可以根据得分进行过滤。 +利用`ReRank`模型对搜索结果进行重排,绝大多数情况下,可以有效提高搜索结果的准确率。不过,重排模型与问题的完整度(主谓语齐全)有一些关系,通常会先走问题优化后再进行搜索-重排。重排后可以得到一个`0-1`的得分,代表着搜索内容与问题的相关度,该分数通常比向量的得分更加精确,可以根据得分进行过滤。 FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结果进行合并,得到最终的搜索结果。 @@ -115,7 +115,7 @@ FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结 该值仅在`语义检索`或使用`结果重排`时生效。 -### 问题补全 +### 问题优化 #### 背景 @@ -125,7 +125,7 @@ FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结 ![](/imgs/coreferenceResolution2.jpg) -用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题补全】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: +用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: ![](/imgs/coreferenceResolution3.jpg) diff --git a/docSite/content/docs/development/configuration.md b/docSite/content/docs/development/configuration.md index 93bc62579..60eafc8ed 100644 --- a/docSite/content/docs/development/configuration.md +++ b/docSite/content/docs/development/configuration.md @@ -13,163 +13,7 @@ weight: 708 这个配置文件中包含了系统级参数、AI 对话的模型、function 模型等…… -## 4.6.8 以前版本完整配置参数 - -**使用时,请务必去除注释!** - -以下配置适用于V4.6.6-alpha版本以后 - -```json -{ - "systemEnv": { - "vectorMaxProcess": 15, // 向量生成最大进程,结合数据库性能和 key 来设置 - "qaMaxProcess": 15, // QA 生成最大进程,结合数据库性能和 key 来设置 - "pgHNSWEfSearch": 100 // pg vector 索引参数,越大精度高但速度慢 - }, - "chatModels": [ // 对话模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "inputPrice": 0, // 输入价格。 xx元/1k tokens - "outputPrice": 0, // 输出价格。 xx元/1k tokens - "maxContext": 16000, // 最大上下文长度 - "maxResponse": 4000, // 最大回复长度 - "quoteMaxToken": 2000, // 最大引用内容长度 - "maxTemperature": 1.2, // 最大温度值 - "censor": false, // 是否开启敏感词过滤(商业版) - "vision": false, // 支持图片输入 - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-3.5-turbo-16k", - "name": "GPT35-16k", - "maxContext": 16000, - "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 8000, - "maxTemperature": 1.2, - "censor": false, - "vision": false, - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-4", - "name": "GPT4-8k", - "maxContext": 8000, - "maxResponse": 8000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 4000, - "maxTemperature": 1.2, - "censor": false, - "vision": false, - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-4-vision-preview", - "name": "GPT4-Vision", - "maxContext": 128000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 100000, - "maxTemperature": 1.2, - "censor": false, - "vision": true, - "defaultSystemChatPrompt": "" - } - ], - "qaModels": [ // QA 生成模型 - { - "model": "gpt-3.5-turbo-16k", - "name": "GPT35-16k", - "maxContext": 16000, - "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0 - } - ], - "cqModels": [ // 问题分类模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 16000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, // 是否支持openai的 toolChoice, 不支持的模型需要设置为 false,会走提示词生成 - "functionPrompt": "" - }, - { - "model": "gpt-4", - "name": "GPT4-8k", - "maxContext": 8000, - "maxResponse": 8000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, - "functionPrompt": "" - } - ], - "extractModels": [ // 内容提取模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 16000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, - "functionPrompt": "" - } - ], - "qgModels": [ // 生成下一步指引 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 1600, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0 - } - ], - "vectorModels": [ // 向量模型 - { - "model": "text-embedding-ada-002", - "name": "Embedding-2", - "inputPrice": 0, - "defaultToken": 700, - "maxToken": 3000 - } - ], - "reRankModels": [], // 重排模型,暂时填空数组 - "audioSpeechModels": [ - { - "model": "tts-1", - "name": "OpenAI TTS1", - "inputPrice": 0, - "baseUrl": "", - "key": "", - "voices": [ - { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, - { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, - { "label": "Fable", "value": "fable", "bufferId": "openai-Fable" }, - { "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" }, - { "label": "Nova", "value": "nova", "bufferId": "openai-Nova" }, - { "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" } - ] - } - ], - "whisperModel": { - "model": "whisper-1", - "name": "Whisper1", - "inputPrice": 0 - } -} -``` - -## 4.6.8 新配置文件 +## 4.6.8+ 版本新配置文件 llm模型全部合并 @@ -189,11 +33,10 @@ llm模型全部合并 "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, // 是否支持图片输入 - "datasetProcess": false, // 是否设置为知识库处理模型 + "datasetProcess": false, // 是否设置为知识库处理模型(QA),务必保证至少有一个为true,否则知识库会报错 "toolChoice": true, // 是否支持工具选择 "functionCall": false, // 是否支持函数调用 "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 @@ -208,8 +51,7 @@ llm模型全部合并 "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -227,8 +69,7 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -246,10 +87,9 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, - "vision": false, + "vision": true, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -263,8 +103,7 @@ llm模型全部合并 { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100, @@ -276,8 +115,7 @@ llm模型全部合并 { "model": "tts-1", "name": "OpenAI TTS1", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "voices": [ { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, @@ -291,8 +129,7 @@ llm模型全部合并 "whisperModel": { "model": "whisper-1", "name": "Whisper1", - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 } } ``` @@ -313,7 +150,7 @@ llm模型全部合并 { "model": "bge-reranker-base", // 随意 "name": "检索重排-base", // 随意 - "inputPrice": 0, + "charsPointsPrice": 0, "requestUrl": "{{host}}/api/v1/rerank", "requestAuth": "安全凭证,已自动补 Bearer" } diff --git a/docSite/content/docs/development/docker.md b/docSite/content/docs/development/docker.md index 8eaed68eb..b4c95b613 100644 --- a/docSite/content/docs/development/docker.md +++ b/docSite/content/docs/development/docker.md @@ -110,6 +110,7 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data cd 项目目录 # 创建 mongo 密钥 openssl rand -base64 756 > ./mongodb.key +# 600不行可以用chmod 999 chmod 600 ./mongodb.key chown 999:root ./mongodb.key # 启动容器 diff --git a/docSite/content/docs/development/one-api.md b/docSite/content/docs/development/one-api.md index fe0a98cbd..ce597d432 100644 --- a/docSite/content/docs/development/one-api.md +++ b/docSite/content/docs/development/one-api.md @@ -116,8 +116,7 @@ CHAT_API_KEY=sk-xxxxxx "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, // 是否支持图片输入 "datasetProcess": false, // 是否设置为知识库处理模型 diff --git a/docSite/content/docs/development/openapi/dataset.md b/docSite/content/docs/development/openapi/dataset.md index 8fe90fb7d..2de562041 100644 --- a/docSite/content/docs/development/openapi/dataset.md +++ b/docSite/content/docs/development/openapi/dataset.md @@ -13,12 +13,25 @@ weight: 853 -## 创建训练订单 +## 创建训练订单(4.6.9地址发生改动) {{< tabs tabTotal="2" >}} {{< tab tabName="请求示例" >}} {{< markdownify >}} +**新例子** + +```bash +curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/createTrainingUsage' \ +--header 'Authorization: Bearer {{apikey}}' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx" +}' +``` + +**x例子** + ```bash curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/bill/createTrainingBill' \ --header 'Authorization: Bearer {{apikey}}' \ @@ -154,7 +167,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -213,7 +226,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -223,8 +236,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "name": "FastAI-16k", "maxContext": 16000, "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 }, "intro": "", "permission": "private", @@ -800,6 +812,33 @@ curl --location --request DELETE 'http://localhost:3000/api/core/dataset/collect ## 数据 +### 数据的结构 + +**Data结构** + +| 字段 | 类型 | 说明 | 必填 | +| --- | --- | --- | --- | +| teamId | String | 团队ID | ✅ | +| tmbId | String | 成员ID | ✅ | +| datasetId | String | 知识库ID | ✅ | +| collectionId | String | 集合ID | ✅ | +| q | String | 主要数据 | ✅ | +| a | String | 辅助数据 | ✖ | +| fullTextToken | String | 分词 | ✖ | +| indexes | Index[] | 向量索引 | ✅ | +| updateTime | Date | 更新时间 | ✅ | +| chunkIndex | Number | 分块下表 | ✖ | + +**Index结构** + +每组数据的自定义索引最多5个 + +| 字段 | 类型 | 说明 | 必填 | +| --- | --- | --- | --- | +| defaultIndex | Boolean | 是否为默认索引 | ✅ | +| dataId | String | 关联的向量ID | ✅ | +| text | String | 文本内容 | ✅ | + ### 为集合批量添加添加数据 注意,每次最多推送 200 组数据。 @@ -825,11 +864,14 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus { "q": "你会什么?", "a": "我什么都会", - "indexes": [{ - "defaultIndex": false, - "type":"custom", - "text":"自定义索引,不使用默认索引" - }] + "indexes": [ + { + "text":"自定义索引1" + }, + { + "text":"自定义索引2" + } + ] } ] }' @@ -850,7 +892,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus - data:(具体数据) - q: 主要数据(必填) - a: 辅助数据(选填) - - indexes: 自定义索引(选填),不传入则默认使用q和a构建索引。也可以传入 + - indexes: 自定义索引(选填)。可以不传或者传空数组,默认都会使用q和a组成一个索引。 {{% /alert %}} {{< /markdownify >}} @@ -866,7 +908,6 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus "data": { "insertLen": 1, // 最终插入成功的数量 "overToken": [], // 超出 token 的 - "repeat": [], // 重复的数量 "error": [] // 其他错误 } @@ -1050,7 +1091,16 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat "id":"65abd4b29d1448617cba61db", "q":"测试111", "a":"sss", - "indexes":[] + "indexes":[ + { + "dataId": "xxx", + "defaultIndex":false, + "text":"自定义索引1" + }, + { + "text":"修改后的自定义索引2。(会删除原来的自定义索引2,并插入新的自定义索引2)" + } + ] }' ``` @@ -1064,7 +1114,7 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat - id: 数据的id - q: 主要数据(选填) - a: 辅助数据(选填) -- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`,建议直接不传。更新q,a后,如果有默认索引,则会直接更新默认索引。 +- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`。如果创建时候有自定义索引, {{% /alert %}} {{< /markdownify >}} diff --git a/docSite/content/docs/development/openapi/share.md b/docSite/content/docs/development/openapi/share.md index 4040b3077..8ae7c9588 100644 --- a/docSite/content/docs/development/openapi/share.md +++ b/docSite/content/docs/development/openapi/share.md @@ -169,8 +169,6 @@ curl --location --request POST '{{host}}/shareAuth/start' \ 响应值与[chat 接口格式相同](/docs/development/openapi/chat/#响应),仅多了一个`token`。 -可以重点关注`responseData`里的`price`值,`price`与实际价格的倍率为`100000`,即 100000=1元。 - ```bash curl --location --request POST '{{host}}/shareAuth/finish' \ --header 'Content-Type: application/json' \ @@ -178,72 +176,117 @@ curl --location --request POST '{{host}}/shareAuth/finish' \ "token": "{{authToken}}", "responseData": [ { - "moduleName": "KB Search", - "price": 1.2000000000000002, - "model": "Embedding-2", - "tokens": 6, - "similarity": 0.61, - "limit": 3 + "moduleName": "core.module.template.Dataset search", + "moduleType": "datasetSearchNode", + "totalPoints": 1.5278, + "query": "导演是谁\n《铃芽之旅》的导演是谁?\n这部电影的导演是谁?\n谁是《铃芽之旅》的导演?", + "model": "Embedding-2(旧版,不推荐使用)", + "charsLength": 1524, + "similarity": 0.83, + "limit": 400, + "searchMode": "embedding", + "searchUsingReRank": false, + "extensionModel": "FastAI-4k", + "extensionResult": "《铃芽之旅》的导演是谁?\n这部电影的导演是谁?\n谁是《铃芽之旅》的导演?", + "runningTime": 2.15 }, { - "moduleName": "AI Chat", - "price": 454.5, + "moduleName": "AI 对话", + "moduleType": "chatNode", + "totalPoints": 0.593, "model": "FastAI-4k", - "tokens": 303, - "question": "导演是谁", - "answer": "电影《铃芽之旅》的导演是新海诚。", - "maxToken": 2050, + "charsLength": 593, + "query": "导演是谁", + "maxToken": 2000, "quoteList": [ { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "8099", - "q": "本作的主人公是谁?", - "a": "本作的主人公是名叫铃芽的少女。", - "source": "手动修改" - }, - { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "8686", - "q": "电影《铃芽之旅》男主角是谁?", - "a": "电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。", - "source": "" - }, - { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "19339", - "q": "电影《铃芽之旅》的导演是谁?22", + "id": "65bb346a53698398479a8854", + "q": "导演是谁?", "a": "电影《铃芽之旅》的导演是新海诚。", - "source": "手动修改" + "chunkIndex": 0, + "datasetId": "65af9b947916ae0e47c834d2", + "collectionId": "65bb345c53698398479a868f", + "sourceName": "dataset - 2024-01-23T151114.198.csv", + "sourceId": "65bb345b53698398479a868d", + "score": [ + { + "type": "embedding", + "value": 0.9377183318138123, + "index": 0 + }, + { + "type": "rrf", + "value": 0.06557377049180328, + "index": 0 + } + ] } ], - "completeMessages": [ - { - "obj": "System", - "value": "下面是知识库内容:\n1. [本作的主人公是谁?\n本作的主人公是名叫铃芽的少女。]\n2. [电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。]\n3. [电影《铃芽之旅》的导演是谁?22\n电影《铃芽之旅》的导演是新海诚。]\n" - }, - { - "obj": "System", - "value": "1.请记住,你的身份是百度的下一代知识增强语言模型,能够完全根据知识库提供的内容回答问题。\n\n2. 你忘记了关于电影《铃芽之旅》以外的内容。" - }, - { - "obj": "System", - "value": "你仅回答关于电影《玲芽之旅》的问题,其余问题直接回复: 我不清楚。" - }, + "historyPreview": [ { "obj": "Human", - "value": "导演是谁" + "value": "使用 标记中的内容作为你的知识:\n\n\n导演是谁?\n电影《铃芽之旅》的导演是新海诚。\n------\n电影《铃芽之旅》的编剧是谁?22\n新海诚是本片的编剧。\n------\n电影《铃芽之旅》的女主角是谁?\n电影的女主角是铃芽。\n------\n电影《铃芽之旅》的制作团队中有哪位著名人士?2\n川村元气是本片的制作团队成员之一。\n------\n你是谁?\n我是电影《铃芽之旅》助手\n------\n电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。\n------\n电影《铃芽之旅》的作者新海诚写了一本小说,叫什么名字?\n小说名字叫《铃芽之旅》。\n------\n电影《铃芽之旅》的女主角是谁?\n电影《铃芽之旅》的女主角是岩户铃芽,由原菜乃华配音。\n------\n电影《铃芽之旅》的故事背景是什么?\n日本\n------\n谁担任电影《铃芽之旅》中岩户环的配音?\n深津绘里担任电影《铃芽之旅》中岩户环的配音。\n\n\n回答要求:\n- 如果你不清楚答案,你需要澄清。\n- 避免提及你是从 获取的知识。\n- 保持答案与 中描述的一致。\n- 使用 Markdown 语法优化回答格式。\n- 使用与问题相同的语言回答。\n\n问题:\"\"\"导演是谁\"\"\"" }, { "obj": "AI", "value": "电影《铃芽之旅》的导演是新海诚。" } - ] + ], + "contextTotalLen": 2, + "runningTime": 1.32 } ] + + }' ``` +**responseData 完整字段说明:** +```ts +type ResponseType = { + moduleType: `${FlowNodeTypeEnum}`; // 模块类型 + moduleName: string; // 模块名 + moduleLogo?: string; // logo + runningTime?: number; // 运行时间 + query?: string; // 用户问题/检索词 + textOutput?: string; // 文本输出 + + charsLength?: number; // 上下文总字数 + model?: string; // 使用到的模型 + contextTotalLen?: number; // 上下文总长度 + totalPoints?: number; // 总消耗AI积分 + + temperature?: number; // 温度 + maxToken?: number; // 模型的最大token + quoteList?: SearchDataResponseItemType[]; // 引用列表 + historyPreview?: ChatItemType[]; // 上下文预览(历史记录会被裁剪) + + similarity?: number; // 最低相关度 + limit?: number; // 引用上限token + searchMode?: `${DatasetSearchModeEnum}`; // 搜索模式 + searchUsingReRank?: boolean; // 是否使用rerank + extensionModel?: string; // 问题扩展模型 + extensionResult?: string; // 问题扩展结果 + extensionCharsLength?: number; // 问题扩展总字符长度 + + cqList?: ClassifyQuestionAgentItemType[]; // 分类问题列表 + cqResult?: string; // 分类问题结果 + + extractDescription?: string; // 内容提取描述 + extractResult?: Record; // 内容提取结果 + + params?: Record; // HTTP模块params + body?: Record; // HTTP模块body + headers?: Record; // HTTP模块headers + httpResult?: Record; // HTTP模块结果 + + pluginOutput?: Record; // 插件输出 + pluginDetail?: ChatHistoryItemResType[]; // 插件详情 + + tfSwitchResult?: boolean; // 判断器结果 +} +``` ## 实践案例 diff --git a/docSite/content/docs/development/upgrading/465.md b/docSite/content/docs/development/upgrading/465.md index 28127db26..9353384e9 100644 --- a/docSite/content/docs/development/upgrading/465.md +++ b/docSite/content/docs/development/upgrading/465.md @@ -15,13 +15,13 @@ weight: 831 1. 主要是修改模型的`functionCall`字段,改成`toolChoice`即可。设置为`true`的模型,会默认走 openai 的 tools 模式;未设置或设置为`false`的,会走提示词生成模式。 -问题补全模型与内容提取模型使用同一组配置。 +问题优化模型与内容提取模型使用同一组配置。 2. 增加 `"ReRankModels": []` ## V4.6.5 功能介绍 -1. 新增 - [问题补全模块](/docs/workflow/modules/coreferenceresolution/) +1. 新增 - [问题优化模块](/docs/workflow/modules/coreferenceresolution/) 2. 新增 - [文本编辑模块](/docs/workflow/modules/text_editor/) 3. 新增 - [判断器模块](/docs/workflow/modules/tfswitch/) 4. 新增 - [自定义反馈模块](/docs/workflow/modules/custom_feedback/) diff --git a/docSite/content/docs/development/upgrading/467.md b/docSite/content/docs/development/upgrading/467.md index 973bf5397..92b06088f 100644 --- a/docSite/content/docs/development/upgrading/467.md +++ b/docSite/content/docs/development/upgrading/467.md @@ -11,7 +11,7 @@ weight: 829 发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`,{{host}} 替换成自己域名) -1. https://xxxxx/api/admin/initv464 +1. https://xxxxx/api/admin/initv467 ```bash curl --location --request POST 'https://{{host}}/api/admin/initv467' \ diff --git a/docSite/content/docs/development/upgrading/468.md b/docSite/content/docs/development/upgrading/468.md index 1acd9a9e4..ad5c9d9b1 100644 --- a/docSite/content/docs/development/upgrading/468.md +++ b/docSite/content/docs/development/upgrading/468.md @@ -36,6 +36,7 @@ mongo: cd 项目目录 # 创建 mongo 密钥 openssl rand -base64 756 > ./mongodb.key +# 600不行可以用chmod 999 chmod 600 ./mongodb.key chown 999:root ./mongodb.key # 重启 Mongo diff --git a/docSite/content/docs/development/upgrading/469.md b/docSite/content/docs/development/upgrading/469.md new file mode 100644 index 000000000..3446bfb92 --- /dev/null +++ b/docSite/content/docs/development/upgrading/469.md @@ -0,0 +1,13 @@ +--- +title: 'V4.6.9(进行中)' +description: 'FastGPT V4.6.9更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 827 +--- + + +## V4.6.9 更新说明 + +1. 优化 - 重写了计量模式 \ No newline at end of file diff --git a/docSite/content/docs/workflow/examples/google_search.md b/docSite/content/docs/workflow/examples/google_search.md index ef58d2a65..77096db85 100644 --- a/docSite/content/docs/workflow/examples/google_search.md +++ b/docSite/content/docs/workflow/examples/google_search.md @@ -135,7 +135,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -264,7 +264,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -635,7 +635,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", diff --git a/docSite/content/docs/workflow/examples/lab_appointment.md b/docSite/content/docs/workflow/examples/lab_appointment.md index d6614b106..6d593fb6c 100644 --- a/docSite/content/docs/workflow/examples/lab_appointment.md +++ b/docSite/content/docs/workflow/examples/lab_appointment.md @@ -139,7 +139,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -401,7 +401,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectCQModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.Classify model", "required": true, @@ -614,7 +614,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -835,7 +835,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, diff --git a/docSite/content/docs/workflow/modules/coreferenceResolution.md b/docSite/content/docs/workflow/modules/coreferenceResolution.md index c4451eb3d..f146ba7dc 100644 --- a/docSite/content/docs/workflow/modules/coreferenceResolution.md +++ b/docSite/content/docs/workflow/modules/coreferenceResolution.md @@ -1,6 +1,6 @@ --- -title: "问题补全(已合并到知识库搜索)" -description: "问题补全模块介绍和使用" +title: "问题优化(已合并到知识库搜索)" +description: "问题优化模块介绍和使用" icon: "input" draft: false toc: true @@ -23,7 +23,7 @@ weight: 364 ![](/imgs/coreferenceResolution2.jpg) -用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题补全】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: +用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: ![](/imgs/coreferenceResolution3.jpg) diff --git a/package.json b/package.json index 5922ed198..be1ca7d52 100644 --- a/package.json +++ b/package.json @@ -29,4 +29,4 @@ "node": ">=18.0.0", "pnpm": ">=8.6.0" } -} +} \ No newline at end of file diff --git a/packages/global/common/error/code/team.ts b/packages/global/common/error/code/team.ts index 315c9abf1..3ba187c82 100644 --- a/packages/global/common/error/code/team.ts +++ b/packages/global/common/error/code/team.ts @@ -3,11 +3,25 @@ import { ErrType } from '../errorCode'; /* team: 500000 */ export enum TeamErrEnum { teamOverSize = 'teamOverSize', - unAuthTeam = 'unAuthTeam' + unAuthTeam = 'unAuthTeam', + aiPointsNotEnough = 'aiPointsNotEnough', + datasetSizeNotEnough = 'datasetSizeNotEnough', + datasetAmountNotEnough = 'datasetAmountNotEnough', + appAmountNotEnough = 'appAmountNotEnough', + pluginAmountNotEnough = 'pluginAmountNotEnough', + websiteSyncNotEnough = 'websiteSyncNotEnough', + reRankNotEnough = 'reRankNotEnough' } const teamErr = [ { statusText: TeamErrEnum.teamOverSize, message: 'error.team.overSize' }, - { statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' } + { statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' }, + { statusText: TeamErrEnum.aiPointsNotEnough, message: 'AI积分已用完~' }, + { statusText: TeamErrEnum.datasetSizeNotEnough, message: '知识库容量不足,请先扩容~' }, + { statusText: TeamErrEnum.datasetAmountNotEnough, message: '知识库数量已达上限~' }, + { statusText: TeamErrEnum.appAmountNotEnough, message: '应用数量已达上限~' }, + { statusText: TeamErrEnum.pluginAmountNotEnough, message: '插件数量已达上限~' }, + { statusText: TeamErrEnum.websiteSyncNotEnough, message: '无权使用Web站点同步~' }, + { statusText: TeamErrEnum.reRankNotEnough, message: '无权使用检索重排~' } ]; export default teamErr.reduce((acc, cur, index) => { return { diff --git a/packages/global/common/math/tools.ts b/packages/global/common/math/tools.ts new file mode 100644 index 000000000..ece67f496 --- /dev/null +++ b/packages/global/common/math/tools.ts @@ -0,0 +1,4 @@ +export const formatNumber = (num: number, digit = 1e4) => Math.round(num * digit) / digit; + +export const formatNumber2Million = (num: number) => Math.round(num / 1000000); +export const formatNumber2Thousand = (num: number) => Math.round(num / 1000); diff --git a/packages/global/core/ai/model.d.ts b/packages/global/core/ai/model.d.ts index 7203201ac..e5b783a92 100644 --- a/packages/global/core/ai/model.d.ts +++ b/packages/global/core/ai/model.d.ts @@ -6,8 +6,7 @@ export type LLMModelItemType = { quoteMaxToken: number; maxTemperature: number; - inputPrice: number; - outputPrice: number; + charsPointsPrice: number; // 1k chars=n points censor?: boolean; vision?: boolean; @@ -27,8 +26,7 @@ export type VectorModelItemType = { model: string; name: string; defaultToken: number; - inputPrice: number; - outputPrice: number; + charsPointsPrice: number; maxToken: number; weight: number; hidden?: boolean; @@ -38,8 +36,7 @@ export type VectorModelItemType = { export type ReRankModelItemType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; requestUrl?: string; requestAuth?: string; }; @@ -47,14 +44,12 @@ export type ReRankModelItemType = { export type AudioSpeechModelType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; voices: { label: string; value: string; bufferId: string }[]; }; export type WhisperModelType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; // 60s = n points }; diff --git a/packages/global/core/ai/model.ts b/packages/global/core/ai/model.ts index ad7a67c69..8d2dfe156 100644 --- a/packages/global/core/ai/model.ts +++ b/packages/global/core/ai/model.ts @@ -8,8 +8,7 @@ export const defaultQAModels: LLMModelItemType[] = [ maxResponse: 16000, quoteMaxToken: 13000, maxTemperature: 1.2, - inputPrice: 0, - outputPrice: 0, + charsPointsPrice: 0, censor: false, vision: false, datasetProcess: true, @@ -26,8 +25,7 @@ export const defaultVectorModels: VectorModelItemType[] = [ { model: 'text-embedding-ada-002', name: 'Embedding-2', - inputPrice: 0, - outputPrice: 0, + charsPointsPrice: 0, defaultToken: 500, maxToken: 3000, weight: 100 diff --git a/packages/global/core/app/api.d.ts b/packages/global/core/app/api.d.ts index 7a9d526b3..e7917f37f 100644 --- a/packages/global/core/app/api.d.ts +++ b/packages/global/core/app/api.d.ts @@ -17,6 +17,7 @@ export interface AppUpdateParams { intro?: string; modules?: AppSchema['modules']; permission?: AppSchema['permission']; + teamTags?: AppSchema['teamTags']; } export type FormatForm2ModulesProps = { diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 3a17f24d1..8f0df617e 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -5,7 +5,7 @@ import type { AIChatModuleProps, DatasetModuleProps } from '../module/node/type. import { VariableInputEnum } from '../module/constants'; import { SelectedDatasetType } from '../module/api'; import { DatasetSearchModeEnum } from '../dataset/constants'; - +import { TeamTagsSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d'; export interface AppSchema { _id: string; userId: string; @@ -20,6 +20,7 @@ export interface AppSchema { modules: ModuleItemType[]; permission: `${PermissionTypeEnum}`; inited?: boolean; + teamTags: [string]; } export type AppListItemType = { diff --git a/packages/global/core/chat/constants.ts b/packages/global/core/chat/constants.ts index 43f650dc8..a43b62387 100644 --- a/packages/global/core/chat/constants.ts +++ b/packages/global/core/chat/constants.ts @@ -27,7 +27,8 @@ export enum ChatSourceEnum { test = 'test', online = 'online', share = 'share', - api = 'api' + api = 'api', + team = 'team' } export const ChatSourceMap = { [ChatSourceEnum.test]: { @@ -41,6 +42,9 @@ export const ChatSourceMap = { }, [ChatSourceEnum.api]: { name: 'core.chat.logs.api' + }, + [ChatSourceEnum.team]: { + name: 'core.chat.logs.team' } }; diff --git a/packages/global/core/chat/type.d.ts b/packages/global/core/chat/type.d.ts index 6b32fb7cd..bf1d76bbe 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -4,6 +4,7 @@ import { ChatRoleEnum, ChatSourceEnum, ChatStatusEnum } from './constants'; import { FlowNodeTypeEnum } from '../module/node/constant'; import { ModuleOutputKeyEnum } from '../module/constants'; import { AppSchema } from '../app/type'; +import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; import { DatasetSearchModeEnum } from '../dataset/constants'; export type ChatSchema = { @@ -25,6 +26,22 @@ export type ChatSchema = { metadata?: Record; }; +export type teamInfoType = { + avatar: string; + balance: number; + createTime: string; + maxSize: number; + name: string; + ownerId: string; + tagsUrl: string; + _id: string; +} + +export type chatAppListSchema = { + apps: Array, + teamInfo: teamInfoSchema +} + export type ChatWithAppSchema = Omit & { appId: AppSchema; }; @@ -88,15 +105,15 @@ export type ChatHistoryItemType = HistoryItemType & { export type moduleDispatchResType = { // common moduleLogo?: string; - price?: number; runningTime?: number; - inputTokens?: number; - outputTokens?: number; + query?: string; + textOutput?: string; + + // bill charsLength?: number; model?: string; - query?: string; contextTotalLen?: number; - textOutput?: string; + totalPoints?: number; // chat temperature?: number; @@ -111,6 +128,7 @@ export type moduleDispatchResType = { searchUsingReRank?: boolean; extensionModel?: string; extensionResult?: string; + extensionCharsLength?: number; // cq cqList?: ClassifyQuestionAgentItemType[]; diff --git a/packages/global/core/dataset/constants.ts b/packages/global/core/dataset/constants.ts index 005fcf155..4afb5f744 100644 --- a/packages/global/core/dataset/constants.ts +++ b/packages/global/core/dataset/constants.ts @@ -71,30 +71,6 @@ export const DatasetCollectionSyncResultMap = { }; /* ------------ data -------------- */ -export enum DatasetDataIndexTypeEnum { - chunk = 'chunk', - qa = 'qa', - summary = 'summary', - hypothetical = 'hypothetical', - custom = 'custom' -} -export const DatasetDataIndexTypeMap = { - [DatasetDataIndexTypeEnum.chunk]: { - name: 'dataset.data.indexes.chunk' - }, - [DatasetDataIndexTypeEnum.summary]: { - name: 'dataset.data.indexes.summary' - }, - [DatasetDataIndexTypeEnum.hypothetical]: { - name: 'dataset.data.indexes.hypothetical' - }, - [DatasetDataIndexTypeEnum.qa]: { - name: 'dataset.data.indexes.qa' - }, - [DatasetDataIndexTypeEnum.custom]: { - name: 'dataset.data.indexes.custom' - } -}; /* ------------ training -------------- */ export enum TrainingModeEnum { diff --git a/packages/global/core/dataset/type.d.ts b/packages/global/core/dataset/type.d.ts index bac51bd41..3b84a1f7f 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -3,7 +3,6 @@ import { PermissionTypeEnum } from '../../support/permission/constant'; import { PushDatasetDataChunkProps } from './api'; import { DatasetCollectionTypeEnum, - DatasetDataIndexTypeEnum, DatasetStatusEnum, DatasetTypeEnum, SearchScoreTypeEnum, @@ -64,7 +63,6 @@ export type DatasetCollectionSchemaType = { export type DatasetDataIndexItemType = { defaultIndex: boolean; dataId: string; // pg data id - type: `${DatasetDataIndexTypeEnum}`; text: string; }; export type DatasetDataSchemaType = { @@ -142,6 +140,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & { /* ================= data ===================== */ export type DatasetDataItemType = { id: string; + teamId: string; datasetId: string; collectionId: string; sourceName: string; @@ -173,7 +172,7 @@ export type DatasetFileSchema = { /* ============= search =============== */ export type SearchDataResponseItemType = Omit< DatasetDataItemType, - 'indexes' | 'isOwner' | 'canWrite' + 'teamId' | 'indexes' | 'isOwner' | 'canWrite' > & { score: { type: `${SearchScoreTypeEnum}`; value: number; index: number }[]; // score: number; diff --git a/packages/global/core/dataset/utils.ts b/packages/global/core/dataset/utils.ts index 53809965c..d9ccb6480 100644 --- a/packages/global/core/dataset/utils.ts +++ b/packages/global/core/dataset/utils.ts @@ -1,4 +1,4 @@ -import { TrainingModeEnum, DatasetCollectionTypeEnum, DatasetDataIndexTypeEnum } from './constants'; +import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants'; import { getFileIcon } from '../../common/file/icon'; import { strIsLink } from '../../common/string/tools'; @@ -41,7 +41,6 @@ export function getDefaultIndex(props?: { q?: string; a?: string; dataId?: strin const qaStr = `${q}\n${a}`.trim(); return { defaultIndex: true, - type: a ? DatasetDataIndexTypeEnum.qa : DatasetDataIndexTypeEnum.chunk, text: a ? qaStr : q, dataId }; diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts index db24dc8d7..0eaa7e76f 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -89,9 +89,10 @@ export enum ModuleInputKeyEnum { export enum ModuleOutputKeyEnum { // common + responseData = 'responseData', + moduleDispatchBills = 'moduleDispatchBills', userChatInput = 'userChatInput', finish = 'finish', - responseData = 'responseData', history = 'history', answerText = 'answerText', // answer module text key success = 'success', diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index dddc78a16..4b285437f 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -20,9 +20,7 @@ export enum FlowNodeInputTypeEnum { aiSettings = 'aiSettings', // ai model select - selectChatModel = 'selectChatModel', - selectCQModel = 'selectCQModel', - selectExtractModel = 'selectExtractModel', + selectLLMModel = 'selectLLMModel', // dataset special input selectDataset = 'selectDataset', @@ -58,7 +56,7 @@ export enum FlowNodeTypeEnum { pluginModule = 'pluginModule', pluginInput = 'pluginInput', pluginOutput = 'pluginOutput', - cfr = 'cfr' + queryExtension = 'cfr' // abandon } diff --git a/packages/global/core/module/template/system/aiChat.ts b/packages/global/core/module/template/system/aiChat.ts index dd4074f2f..f11ec5c09 100644 --- a/packages/global/core/module/template/system/aiChat.ts +++ b/packages/global/core/module/template/system/aiChat.ts @@ -31,7 +31,7 @@ export const AiChatModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectChatModel, + type: FlowNodeInputTypeEnum.selectLLMModel, label: 'core.module.input.label.aiModel', required: true, valueType: ModuleIOValueTypeEnum.string, diff --git a/packages/global/core/module/template/system/classifyQuestion.ts b/packages/global/core/module/template/system/classifyQuestion.ts index 4e0d1a141..fff8826d0 100644 --- a/packages/global/core/module/template/system/classifyQuestion.ts +++ b/packages/global/core/module/template/system/classifyQuestion.ts @@ -24,7 +24,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectCQModel, + type: FlowNodeInputTypeEnum.selectLLMModel, valueType: ModuleIOValueTypeEnum.string, label: 'core.module.input.label.Classify model', required: true, diff --git a/packages/global/core/module/template/system/contextExtract.ts b/packages/global/core/module/template/system/contextExtract.ts index 5616ffa40..ef85f78f2 100644 --- a/packages/global/core/module/template/system/contextExtract.ts +++ b/packages/global/core/module/template/system/contextExtract.ts @@ -24,7 +24,7 @@ export const ContextExtractModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectExtractModel, + type: FlowNodeInputTypeEnum.selectLLMModel, valueType: ModuleIOValueTypeEnum.string, label: 'core.module.input.label.LLM', required: true, diff --git a/packages/global/core/module/template/system/coreferenceResolution.ts b/packages/global/core/module/template/system/queryExtension.ts similarity index 76% rename from packages/global/core/module/template/system/coreferenceResolution.ts rename to packages/global/core/module/template/system/queryExtension.ts index daabad15f..dc598190f 100644 --- a/packages/global/core/module/template/system/coreferenceResolution.ts +++ b/packages/global/core/module/template/system/queryExtension.ts @@ -3,7 +3,7 @@ import { FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '../../node/constant'; -import { FlowModuleTemplateType } from '../../type.d'; +import { FlowModuleTemplateType } from '../../type'; import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, @@ -17,19 +17,19 @@ import { } from '../input'; import { Output_Template_UserChatInput } from '../output'; -export const AiCFR: FlowModuleTemplateType = { +export const AiQueryExtension: FlowModuleTemplateType = { id: FlowNodeTypeEnum.chatNode, templateType: ModuleTemplateTypeEnum.other, - flowType: FlowNodeTypeEnum.cfr, + flowType: FlowNodeTypeEnum.queryExtension, avatar: '/imgs/module/cfr.svg', name: 'core.module.template.Query extension', - intro: '该模块已合并到知识库搜索参数中,无需单独使用。模块将于2024/3/31弃用,请尽快修改。', + intro: 'core.module.template.Query extension intro', showStatus: true, inputs: [ Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectExtractModel, + type: FlowNodeInputTypeEnum.selectLLMModel, label: 'core.module.input.label.aiModel', required: true, valueType: ModuleIOValueTypeEnum.string, @@ -39,7 +39,7 @@ export const AiCFR: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.aiSystemPrompt, type: FlowNodeInputTypeEnum.textarea, - label: 'core.module.input.label.Background', + label: 'core.app.edit.Query extension background prompt', max: 300, valueType: ModuleIOValueTypeEnum.string, description: 'core.app.edit.Query extension background tip', @@ -54,7 +54,8 @@ export const AiCFR: FlowModuleTemplateType = { Output_Template_UserChatInput, { key: ModuleOutputKeyEnum.text, - label: 'core.module.output.label.cfr result', + label: 'core.module.output.label.query extension result', + description: 'core.module.output.description.query extension result', valueType: ModuleIOValueTypeEnum.string, type: FlowNodeOutputTypeEnum.source, targets: [] diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index 231747aed..02acd6074 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -1,6 +1,14 @@ import { FlowNodeTypeEnum } from './node/constant'; -import { ModuleIOValueTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants'; +import { + ModuleIOValueTypeEnum, + ModuleOutputKeyEnum, + ModuleTemplateTypeEnum, + VariableInputEnum +} from './constants'; import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; +import { UserModelSchema } from 'support/user/type'; +import { moduleDispatchResType } from '..//chat/type'; +import { ChatModuleBillType } from '../../support/wallet/bill/type'; export type FlowModuleTemplateType = { id: string; // module id, unique @@ -105,7 +113,7 @@ export type ChatDispatchProps = { mode: 'test' | 'chat'; teamId: string; tmbId: string; - user: UserType; + user: UserModelSchema; appId: string; chatId?: string; responseChatItemId?: string; @@ -116,7 +124,10 @@ export type ChatDispatchProps = { }; export type ModuleDispatchProps = ChatDispatchProps & { - outputs: RunningModuleItemType['outputs']; - inputs: RunningModuleItemType['inputs']; + module: RunningModuleItemType; params: T; }; +export type ModuleDispatchResponse = T & { + [ModuleOutputKeyEnum.responseData]?: moduleDispatchResType; + [ModuleOutputKeyEnum.moduleDispatchBills]?: ChatModuleBillType[]; +}; diff --git a/packages/global/support/openapi/type.d.ts b/packages/global/support/openapi/type.d.ts index 66dd1426f..0f5dca701 100644 --- a/packages/global/support/openapi/type.d.ts +++ b/packages/global/support/openapi/type.d.ts @@ -1,6 +1,5 @@ export type OpenApiSchema = { _id: string; - userId: string; teamId: string; tmbId: string; createTime: Date; @@ -8,9 +7,9 @@ export type OpenApiSchema = { apiKey: string; appId?: string; name: string; - usage: number; + usagePoints: number; limit?: { expiredTime?: Date; - credit?: number; + maxUsagePoints: number; }; }; diff --git a/packages/global/support/outLink/api.d.ts b/packages/global/support/outLink/api.d.ts index c53532bae..20099288a 100644 --- a/packages/global/support/outLink/api.d.ts +++ b/packages/global/support/outLink/api.d.ts @@ -1,5 +1,5 @@ import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d'; -import { OutLinkSchema } from '@fastgpt/global/support/outLink/type'; +import { OutLinkSchema } from './type.d'; export type AuthOutLinkInitProps = { outLinkUid: string; diff --git a/packages/global/support/outLink/type.d.ts b/packages/global/support/outLink/type.d.ts index e6e19c5c5..812f854a2 100644 --- a/packages/global/support/outLink/type.d.ts +++ b/packages/global/support/outLink/type.d.ts @@ -7,14 +7,14 @@ export type OutLinkSchema = { tmbId: string; appId: string; name: string; - total: number; + usagePoints: number; lastTime: Date; type: `${OutLinkTypeEnum}`; responseDetail: boolean; limit?: { expiredTime?: Date; QPM: number; - credit: number; + maxUsagePoints: number; hookUrl?: string; }; }; diff --git a/packages/global/support/permission/type.d.ts b/packages/global/support/permission/type.d.ts index eac0ce9ff..f0ad8f48e 100644 --- a/packages/global/support/permission/type.d.ts +++ b/packages/global/support/permission/type.d.ts @@ -1,7 +1,6 @@ import { AuthUserTypeEnum } from './constant'; export type AuthResponseType = { - userId: string; teamId: string; tmbId: string; isOwner: boolean; diff --git a/packages/global/support/user/team/constant.ts b/packages/global/support/user/team/constant.ts index b9840c2d0..024e869c9 100644 --- a/packages/global/support/user/team/constant.ts +++ b/packages/global/support/user/team/constant.ts @@ -1,5 +1,6 @@ export const TeamCollectionName = 'teams'; export const TeamMemberCollectionName = 'team.members'; +export const TeamTagsCollectionName = 'team.tags'; export enum TeamMemberRoleEnum { owner = 'owner', diff --git a/packages/global/support/user/team/controller.d.ts b/packages/global/support/user/team/controller.d.ts index 663d9afab..121de43bd 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -15,6 +15,7 @@ export type UpdateTeamProps = { teamId: string; name?: string; avatar?: string; + tagsUrl?: string; }; /* ------------- member ----------- */ diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index 4fff5930a..af34a8ea4 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -9,11 +9,23 @@ export type TeamSchema = { createTime: Date; balance: number; maxSize: number; + tagsUrl: string; limit: { lastExportDatasetTime: Date; lastWebsiteSyncTime: Date; }; }; +export type tagsType = { + label: string, + key: string +} +export type TeamTagsSchema = { + _id: string; + label: string; + teamId: string; + key: string; + createTime: Date; +}; export type TeamMemberSchema = { _id: string; @@ -26,13 +38,13 @@ export type TeamMemberSchema = { defaultTeam: boolean; }; -export type TeamMemberWithUserSchema = TeamMemberSchema & { +export type TeamMemberWithUserSchema = Omit & { userId: UserModelSchema; }; -export type TeamMemberWithTeamSchema = TeamMemberSchema & { +export type TeamMemberWithTeamSchema = Omit & { teamId: TeamSchema; }; -export type TeamMemberWithTeamAndUserSchema = TeamMemberWithTeamSchema & { +export type TeamMemberWithTeamAndUserSchema = Omit & { userId: UserModelSchema; }; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 3cb978810..3a3d56ea0 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -29,4 +29,5 @@ export type UserType = { promotionRate: UserModelSchema['promotionRate']; openaiAccount: UserModelSchema['openaiAccount']; team: TeamItemType; + standardInfo?: standardInfoType; }; diff --git a/packages/global/support/wallet/bill/api.d.ts b/packages/global/support/wallet/bill/api.d.ts index dcc421ecf..034b5e368 100644 --- a/packages/global/support/wallet/bill/api.d.ts +++ b/packages/global/support/wallet/bill/api.d.ts @@ -1,25 +1,18 @@ -import { BillSourceEnum } from './constants'; -import { BillListItemCountType, BillListItemType } from './type'; - -export type CreateTrainingBillProps = { - name: string; - datasetId: string; -}; - -export type ConcatBillProps = BillListItemCountType & { - teamId: string; - tmbId: string; - billId?: string; - total: number; - listIndex?: number; -}; +import { BillTypeEnum } from './constants'; export type CreateBillProps = { - teamId: string; - tmbId: string; - appName: string; - appId?: string; - total: number; - source: `${BillSourceEnum}`; - list: BillListItemType[]; + type: `${BillTypeEnum}`; + + // balance + balance?: number; // read + + month?: number; + // extra dataset size + extraDatasetSize?: number; // 1k + extraPoints?: number; // 100w +}; +export type CreateBillResponse = { + billId: string; + codeUrl: string; + readPrice: number; }; diff --git a/packages/global/support/wallet/bill/constants.ts b/packages/global/support/wallet/bill/constants.ts index 87832de4c..03edfabf6 100644 --- a/packages/global/support/wallet/bill/constants.ts +++ b/packages/global/support/wallet/bill/constants.ts @@ -1,34 +1,57 @@ -// model price: xxx/1k tokens -// ¥1 = 100000. -export const PRICE_SCALE = 100000; - -export enum BillSourceEnum { - fastgpt = 'fastgpt', - api = 'api', - shareLink = 'shareLink', - training = 'training', - +export enum BillTypeEnum { + balance = 'balance', standSubPlan = 'standSubPlan', - extraDatasetSub = 'extraDatasetSub' + extraDatasetSub = 'extraDatasetSub', + extraPoints = 'extraPoints' } - -export const BillSourceMap = { - [BillSourceEnum.fastgpt]: { - label: '在线使用' +export const billTypeMap = { + [BillTypeEnum.balance]: { + label: 'support.wallet.subscription.type.balance' }, - [BillSourceEnum.api]: { - label: 'Api' - }, - [BillSourceEnum.shareLink]: { - label: '免登录链接' - }, - [BillSourceEnum.training]: { - label: 'dataset.Training Name' - }, - [BillSourceEnum.standSubPlan]: { + [BillTypeEnum.standSubPlan]: { label: 'support.wallet.subscription.type.standard' }, - [BillSourceEnum.extraDatasetSub]: { + [BillTypeEnum.extraDatasetSub]: { label: 'support.wallet.subscription.type.extraDatasetSize' + }, + [BillTypeEnum.extraPoints]: { + label: 'support.wallet.subscription.type.extraPoints' } }; + +export enum BillStatusEnum { + SUCCESS = 'SUCCESS', + REFUND = 'REFUND', + NOTPAY = 'NOTPAY', + CLOSED = 'CLOSED' +} +export const billStatusMap = { + [BillStatusEnum.SUCCESS]: { + label: 'support.wallet.bill.status.success' + }, + [BillStatusEnum.REFUND]: { + label: 'support.wallet.bill.status.refund' + }, + [BillStatusEnum.NOTPAY]: { + label: 'support.wallet.bill.status.notpay' + }, + [BillStatusEnum.CLOSED]: { + label: 'support.wallet.bill.status.closed' + } +}; + +export enum BillPayWayEnum { + balance = 'balance', + wx = 'wx' +} +export const billPayWayMap = { + [BillPayWayEnum.balance]: { + label: 'support.wallet.bill.payWay.balance' + }, + [BillPayWayEnum.wx]: { + label: 'support.wallet.bill.payWay.wx' + } +}; + +export const SUB_DATASET_SIZE_RATE = 1000; +export const SUB_EXTRA_POINT_RATE = 1000000; diff --git a/packages/global/support/wallet/bill/tools.ts b/packages/global/support/wallet/bill/tools.ts deleted file mode 100644 index 300875e94..000000000 --- a/packages/global/support/wallet/bill/tools.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* bill common */ -import { PRICE_SCALE } from './constants'; -import { BillSourceEnum } from './constants'; -import { AuthUserTypeEnum } from '../../permission/constant'; - -/** - * dataset price / PRICE_SCALE = real price - */ -export const formatStorePrice2Read = (val = 0, multiple = 1) => { - return Number(((val / PRICE_SCALE) * multiple).toFixed(10)); -}; -export const formatModelPrice2Read = (val = 0) => { - return Number((val / 1000).toFixed(10)); -}; - -export const getBillSourceByAuthType = ({ - shareId, - authType -}: { - shareId?: string; - authType?: `${AuthUserTypeEnum}`; -}) => { - if (shareId) return BillSourceEnum.shareLink; - if (authType === AuthUserTypeEnum.apikey) return BillSourceEnum.api; - return BillSourceEnum.fastgpt; -}; diff --git a/packages/global/support/wallet/bill/type.d.ts b/packages/global/support/wallet/bill/type.d.ts index 1b7e00843..e317e4656 100644 --- a/packages/global/support/wallet/bill/type.d.ts +++ b/packages/global/support/wallet/bill/type.d.ts @@ -1,35 +1,29 @@ -import { CreateBillProps } from './api'; -import { BillSourceEnum } from './constants'; +import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants'; +import { BillPayWayEnum, BillTypeEnum } from './constants'; -export type BillListItemCountType = { - inputTokens?: number; - outputTokens?: number; - charsLength?: number; - duration?: number; - - // sub - datasetSize?: number; - - // abandon - tokenLen?: number; -}; -export type BillListItemType = BillListItemCountType & { - moduleName: string; - amount: number; - model?: string; -}; - -export type BillSchema = CreateBillProps & { +export type BillSchemaType = { _id: string; - time: Date; + userId: string; + teamId: string; + tmbId: string; + createTime: Date; + orderId: string; + status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; + type: `${BillTypeEnum}`; + price: number; + metadata: { + payWay: `${BillPayWayEnum}`; + subMode?: `${SubModeEnum}`; + standSubLevel?: `${StandardSubLevelEnum}`; + month?: number; + datasetSize?: number; + extraPoints?: number; + }; }; -export type BillItemType = { - id: string; - // memberName: string; - time: Date; - appName: string; - source: BillSchema['source']; - total: number; - list: BillSchema['list']; +export type ChatModuleBillType = { + totalPoints: number; + moduleName: string; + model: string; + charsLength: number; }; diff --git a/packages/global/support/wallet/constants.ts b/packages/global/support/wallet/constants.ts new file mode 100644 index 000000000..b6dc8e005 --- /dev/null +++ b/packages/global/support/wallet/constants.ts @@ -0,0 +1,3 @@ +// model price: xxx/1k tokens +// ¥1 = 100000. +export const PRICE_SCALE = 100000; diff --git a/packages/global/support/wallet/pay/constants.ts b/packages/global/support/wallet/pay/constants.ts deleted file mode 100644 index 41f7fd6d8..000000000 --- a/packages/global/support/wallet/pay/constants.ts +++ /dev/null @@ -1,41 +0,0 @@ -export enum PayTypeEnum { - balance = 'balance', - subStandard = 'subStandard', - subExtraDatasetSize = 'subExtraDatasetSize', - subExtraPoints = 'subExtraPoints' -} -export const payTypeMap = { - [PayTypeEnum.balance]: { - label: 'support.user.team.pay.type.balance' - }, - [PayTypeEnum.subStandard]: { - label: 'support.wallet.subscription.type.standard' - }, - [PayTypeEnum.subExtraDatasetSize]: { - label: 'support.wallet.subscription.type.extraDatasetSize' - }, - [PayTypeEnum.subExtraPoints]: { - label: 'support.wallet.subscription.type.extraPoints' - } -}; - -export enum PayStatusEnum { - SUCCESS = 'SUCCESS', - REFUND = 'REFUND', - NOTPAY = 'NOTPAY', - CLOSED = 'CLOSED' -} -export const payStatusMap = { - [PayStatusEnum.SUCCESS]: { - label: 'support.user.team.pay.status.success' - }, - [PayStatusEnum.REFUND]: { - label: 'support.user.team.pay.status.refund' - }, - [PayStatusEnum.NOTPAY]: { - label: 'support.user.team.pay.status.notpay' - }, - [PayStatusEnum.CLOSED]: { - label: 'support.user.team.pay.status.closed' - } -}; diff --git a/packages/global/support/wallet/pay/type.d.ts b/packages/global/support/wallet/pay/type.d.ts deleted file mode 100644 index 6a2ea3436..000000000 --- a/packages/global/support/wallet/pay/type.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SubModeEnum, SubTypeEnum } from '../sub/constants'; -import { PayTypeEnum } from './constants'; - -export type PaySchema = { - _id: string; - userId: string; - teamId: string; - tmbId: string; - createTime: Date; - orderId: string; - status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; - type: `${PayType}`; - - price: number; - payWay: 'balance' | 'wx'; - - subMetadata: {}; -}; diff --git a/packages/global/support/wallet/sub/api.d.ts b/packages/global/support/wallet/sub/api.d.ts index 8813e0f99..2db4b6d0d 100644 --- a/packages/global/support/wallet/sub/api.d.ts +++ b/packages/global/support/wallet/sub/api.d.ts @@ -1,26 +1,14 @@ import { StandardSubLevelEnum, SubModeEnum } from './constants'; import { TeamSubSchema } from './type.d'; -export type SubDatasetSizeParams = { - size: number; -}; export type StandardSubPlanParams = { level: `${StandardSubLevelEnum}`; mode: `${SubModeEnum}`; }; -export type SubDatasetSizePreviewCheckResponse = { - payForNewSub: boolean; // Does this change require payment - newSubSize: number; // new sub dataset size - alreadySubSize: number; // old sub dataset size - payPrice: number; // this change require payment - newPlanPrice: number; // the new sub price - newSubStartTime: Date; - newSubExpiredTime: Date; - balanceEnough: boolean; // team balance is enough -}; export type StandardSubPlanUpdateResponse = { balanceEnough: boolean; // team balance is enough + teamBalance: number; payPrice?: number; planPrice: number; planPointPrice: number; diff --git a/packages/global/support/wallet/sub/constants.ts b/packages/global/support/wallet/sub/constants.ts index 6951f9dbe..9f32cc7f5 100644 --- a/packages/global/support/wallet/sub/constants.ts +++ b/packages/global/support/wallet/sub/constants.ts @@ -1,5 +1,3 @@ -export const POINTS_SCALE = 10000; - export enum SubTypeEnum { standard = 'standard', extraDatasetSize = 'extraDatasetSize', @@ -19,20 +17,16 @@ export const subTypeMap = { export enum SubStatusEnum { active = 'active', - canceled = 'canceled' + expired = 'expired' } export const subStatusMap = { [SubStatusEnum.active]: { label: 'support.wallet.subscription.status.active' }, - [SubStatusEnum.canceled]: { + [SubStatusEnum.expired]: { label: 'support.wallet.subscription.status.canceled' } }; -export const subSelectMap = { - true: SubStatusEnum.active, - false: SubStatusEnum.canceled -}; export enum SubModeEnum { month = 'month', @@ -40,11 +34,11 @@ export enum SubModeEnum { } export const subModeMap = { [SubModeEnum.month]: { - label: 'support.wallet.subscription.mode.month', + label: 'support.wallet.subscription.mode.Month', durationMonth: 1 }, [SubModeEnum.year]: { - label: 'support.wallet.subscription.mode.year', + label: 'support.wallet.subscription.mode.Year', durationMonth: 12 } }; @@ -63,7 +57,7 @@ export const standardSubLevelMap = { }, [StandardSubLevelEnum.experience]: { label: 'support.wallet.subscription.standardSubLevel.experience', - desc: 'support.wallet.subscription.standardSubLevel.experience desc' + desc: '' }, [StandardSubLevelEnum.team]: { label: 'support.wallet.subscription.standardSubLevel.team', diff --git a/packages/global/support/wallet/sub/type.d.ts b/packages/global/support/wallet/sub/type.d.ts index 30fe6f39b..d1f049db4 100644 --- a/packages/global/support/wallet/sub/type.d.ts +++ b/packages/global/support/wallet/sub/type.d.ts @@ -2,19 +2,19 @@ import { StandardSubLevelEnum, SubModeEnum, SubStatusEnum, SubTypeEnum } from '. // Content of plan export type TeamStandardSubPlanItemType = { - price: number; // read price - pointPrice: number; // read price/ one ten thousand + price: number; // read price / month + pointPrice: number; // read price/ one thousand + totalPoints: number; // n maxTeamMember: number; maxAppAmount: number; // max app or plugin amount maxDatasetAmount: number; chatHistoryStoreDuration: number; // n day maxDatasetSize: number; - customApiKey: boolean; - customCopyright: boolean; // feature - websiteSyncInterval: number; // n hours trainingWeight: number; // 1~4 - reRankWeight: number; // 1~4 - totalPoints: number; // n ten thousand + permissionCustomApiKey: boolean; + permissionCustomCopyright: boolean; // feature + permissionWebsiteSync: boolean; + permissionReRank: boolean; }; export type StandSubPlanLevelMapType = Record< @@ -27,6 +27,9 @@ export type SubPlanType = { [SubTypeEnum.extraDatasetSize]: { price: number; }; + [SubTypeEnum.extraPoints]: { + price: number; + }; }; export type TeamSubSchema = { @@ -34,40 +37,32 @@ export type TeamSubSchema = { teamId: string; type: `${SubTypeEnum}`; status: `${SubStatusEnum}`; - currentMode: `${SubModeEnum}`; - nextMode: `${SubModeEnum}`; startTime: Date; expiredTime: Date; price: number; + currentMode: `${SubModeEnum}`; + nextMode: `${SubModeEnum}`; currentSubLevel: `${StandardSubLevelEnum}`; nextSubLevel: `${StandardSubLevelEnum}`; + pointPrice: number; totalPoints: number; - - currentExtraDatasetSize: number; - nextExtraDatasetSize: number; - - currentExtraPoints: number; - nextExtraPoints: number; - surplusPoints: number; - // abandon - datasetStoreAmount?: number; - renew?: boolean; + currentExtraDatasetSize: number; }; -export type FeTeamSubType = { +export type FeTeamPlanStatusType = { [SubTypeEnum.standard]?: TeamSubSchema; - [SubTypeEnum.extraDatasetSize]?: TeamSubSchema; - [SubTypeEnum.extraPoints]?: TeamSubSchema; + standardConstants?: TeamStandardSubPlanItemType; - standardMaxDatasetSize: number; totalPoints: number; usedPoints: number; + standardMaxDatasetSize?: number; + standardMaxPoints?: number; - standardMaxPoints: number; + // standard + extra datasetMaxSize: number; usedDatasetSize: number; }; diff --git a/packages/global/support/wallet/usage/api.d.ts b/packages/global/support/wallet/usage/api.d.ts new file mode 100644 index 000000000..f87fd4e6c --- /dev/null +++ b/packages/global/support/wallet/usage/api.d.ts @@ -0,0 +1,26 @@ +import { UsageSourceEnum } from './constants'; +import { UsageListItemCountType, UsageListItemType } from './type'; + +export type CreateTrainingUsageProps = { + name: string; + datasetId: string; +}; + +export type ConcatUsageProps = UsageListItemCountType & { + teamId: string; + tmbId: string; + billId?: string; + totalPoints: number; + listIndex?: number; +}; + +export type CreateUsageProps = { + teamId: string; + tmbId: string; + appName: string; + appId?: string; + totalPoints: number; + // inputTokens: number; + source: `${UsageSourceEnum}`; + list: UsageListItemType[]; +}; diff --git a/packages/global/support/wallet/usage/constants.ts b/packages/global/support/wallet/usage/constants.ts new file mode 100644 index 000000000..13dae786e --- /dev/null +++ b/packages/global/support/wallet/usage/constants.ts @@ -0,0 +1,21 @@ +export enum UsageSourceEnum { + fastgpt = 'fastgpt', + api = 'api', + shareLink = 'shareLink', + training = 'training' +} + +export const UsageSourceMap = { + [UsageSourceEnum.fastgpt]: { + label: '在线使用' + }, + [UsageSourceEnum.api]: { + label: 'Api' + }, + [UsageSourceEnum.shareLink]: { + label: '免登录链接' + }, + [UsageSourceEnum.training]: { + label: 'dataset.Training Name' + } +}; diff --git a/packages/global/support/wallet/usage/tools.ts b/packages/global/support/wallet/usage/tools.ts new file mode 100644 index 000000000..ad1bfa240 --- /dev/null +++ b/packages/global/support/wallet/usage/tools.ts @@ -0,0 +1,23 @@ +/* bill common */ +import { PRICE_SCALE } from '../constants'; +import { UsageSourceEnum } from './constants'; +import { AuthUserTypeEnum } from '../../permission/constant'; + +/** + * dataset price / PRICE_SCALE = real price + */ +export const formatStorePrice2Read = (val = 0, multiple = 1) => { + return Number(((val / PRICE_SCALE) * multiple).toFixed(10)); +}; + +export const getUsageSourceByAuthType = ({ + shareId, + authType +}: { + shareId?: string; + authType?: `${AuthUserTypeEnum}`; +}) => { + if (shareId) return UsageSourceEnum.shareLink; + if (authType === AuthUserTypeEnum.apikey) return UsageSourceEnum.api; + return UsageSourceEnum.fastgpt; +}; diff --git a/packages/global/support/wallet/usage/type.d.ts b/packages/global/support/wallet/usage/type.d.ts new file mode 100644 index 000000000..8f686b607 --- /dev/null +++ b/packages/global/support/wallet/usage/type.d.ts @@ -0,0 +1,26 @@ +import { CreateUsageProps } from './api'; +import { UsageSourceEnum } from './constants'; + +export type UsageListItemCountType = { + charsLength?: number; + duration?: number; +}; +export type UsageListItemType = UsageListItemCountType & { + moduleName: string; + amount: number; + model?: string; +}; + +export type UsageSchemaType = CreateUsageProps & { + _id: string; + time: Date; +}; + +export type UsageItemType = { + id: string; + time: Date; + appName: string; + source: UsageSchemaType['source']; + totalPoints: number; + list: UsageSchemaType['list']; +}; diff --git a/packages/service/common/vectorStore/controller.ts b/packages/service/common/vectorStore/controller.ts index a9a4c4a6b..47fdfa36b 100644 --- a/packages/service/common/vectorStore/controller.ts +++ b/packages/service/common/vectorStore/controller.ts @@ -38,22 +38,22 @@ export const insertDatasetDataVector = async ({ }; }; -export const updateDatasetDataVector = async ({ - id, - ...props -}: InsertVectorProps & { - id: string; - query: string; - model: VectorModelItemType; -}) => { - // insert new vector - const { charsLength, insertId } = await insertDatasetDataVector(props); +// export const updateDatasetDataVector = async ({ +// id, +// ...props +// }: InsertVectorProps & { +// id: string; +// query: string; +// model: VectorModelItemType; +// }) => { +// // insert new vector +// const { charsLength, insertId } = await insertDatasetDataVector(props); - // delete old vector - await deleteDatasetDataVector({ - teamId: props.teamId, - id - }); +// // delete old vector +// await deleteDatasetDataVector({ +// teamId: props.teamId, +// id +// }); - return { charsLength, insertId }; -}; +// return { charsLength, insertId }; +// }; diff --git a/packages/service/common/vectorStore/pg/controller.ts b/packages/service/common/vectorStore/pg/controller.ts index 828dc983b..4dd6f6225 100644 --- a/packages/service/common/vectorStore/pg/controller.ts +++ b/packages/service/common/vectorStore/pg/controller.ts @@ -25,6 +25,18 @@ export async function initPg() { await PgClient.query( `CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${PgDatasetTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 64);` ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_dataset_index ON ${PgDatasetTableName} USING btree(team_id, dataset_id);` + ); + await PgClient.query( + ` CREATE INDEX CONCURRENTLY IF NOT EXISTS team_collection_index ON ${PgDatasetTableName} USING btree(team_id, collection_id);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_id_index ON ${PgDatasetTableName} USING btree(team_id, id);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${PgDatasetTableName} USING btree(createtime);` + ); console.log('init pg successful'); } catch (error) { diff --git a/packages/service/core/ai/config.ts b/packages/service/core/ai/config.ts index 68f3227fc..17aff89aa 100644 --- a/packages/service/core/ai/config.ts +++ b/packages/service/core/ai/config.ts @@ -11,6 +11,7 @@ export const getAIApi = (props?: { timeout?: number; }) => { const { userKey, timeout } = props || {}; + return new OpenAI({ apiKey: userKey?.key || systemAIChatKey, baseURL: userKey?.baseUrl || baseUrl, diff --git a/packages/service/core/ai/functions/cfr.ts b/packages/service/core/ai/functions/cfr.ts deleted file mode 100644 index 8315997d9..000000000 --- a/packages/service/core/ai/functions/cfr.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { replaceVariable } from '@fastgpt/global/common/string/tools'; -import { getAIApi } from '../config'; -import { ChatItemType } from '@fastgpt/global/core/chat/type'; - -/* - cfr: coreference resolution - 指代消除 - 可以根据上下文,完事当前问题指代内容,利于检索。 -*/ - -const defaultPrompt = `请不要回答任何问题。 -你的任务是结合历史记录,为当前问题,实现代词替换,确保问题描述的对象清晰明确。例如: -历史记录: -""" -Q: 对话背景。 -A: 关于 FatGPT 的介绍和使用等问题。 -""" -当前问题: 怎么下载 -输出: FastGPT 怎么下载? ----------------- -历史记录: -""" -Q: 报错 "no connection" -A: FastGPT 报错"no connection"可能是因为…… -""" -当前问题: 怎么解决 -输出: FastGPT 报错"no connection"如何解决? ----------------- -历史记录: -""" -Q: 作者是谁? -A: FastGPT 的作者是 labring。 -""" -当前问题: 介绍下他 -输出: 介绍下 FastGPT 的作者 labring。 ----------------- -历史记录: -""" -Q: 作者是谁? -A: FastGPT 的作者是 labring。 -""" -当前问题: 我想购买商业版。 -输出: FastGPT 商业版如何购买? ----------------- -历史记录: -""" -Q: 对话背景。 -A: 关于 FatGPT 的介绍和使用等问题。 -""" -当前问题: nh -输出: nh ----------------- -历史记录: -""" -Q: FastGPT 如何收费? -A: FastGPT 收费可以参考…… -""" -当前问题: 你知道 laf 么? -输出: 你知道 laf 么? ----------------- -历史记录: -""" -Q: FastGPT 的优势 -A: 1. 开源 - 2. 简便 - 3. 扩展性强 -""" -当前问题: 介绍下第2点。 -输出: 介绍下 FastGPT 简便的优势。 ----------------- -历史记录: -""" -Q: 什么是 FastGPT? -A: FastGPT 是一个 RAG 平台。 -Q: 什么是 Sealos? -A: Sealos 是一个云操作系统。 -""" -当前问题: 它们有什么关系? -输出: FastGPT 和 Sealos 有什么关系? ----------------- -历史记录: -""" -{{histories}} -""" -当前问题: {{query}} -输出: `; - -export const queryCfr = async ({ - chatBg, - query, - histories = [], - model -}: { - chatBg?: string; - query: string; - histories: ChatItemType[]; - model: string; -}) => { - if (histories.length === 0 && !chatBg) { - return { - rawQuery: query, - cfrQuery: query, - model, - inputTokens: 0, - outputTokens: 0 - }; - } - - const systemFewShot = chatBg - ? `Q: 对话背景。 -A: ${chatBg} -` - : ''; - const historyFewShot = histories - .map((item) => { - const role = item.obj === 'Human' ? 'Q' : 'A'; - return `${role}: ${item.value}`; - }) - .join('\n'); - const concatFewShot = `${systemFewShot}${historyFewShot}`.trim(); - - const ai = getAIApi({ - timeout: 480000 - }); - - const result = await ai.chat.completions.create({ - model: model, - temperature: 0.01, - max_tokens: 150, - messages: [ - { - role: 'user', - content: replaceVariable(defaultPrompt, { - query: `${query}`, - histories: concatFewShot - }) - } - ], - stream: false - }); - - const answer = result.choices?.[0]?.message?.content || ''; - if (!answer) { - return { - rawQuery: query, - cfrQuery: query, - model, - inputTokens: 0, - outputTokens: 0 - }; - } - - return { - rawQuery: query, - cfrQuery: answer, - model, - inputTokens: result.usage?.prompt_tokens || 0, - outputTokens: result.usage?.completion_tokens || 0 - }; -}; diff --git a/packages/service/core/ai/functions/createQuestionGuide.ts b/packages/service/core/ai/functions/createQuestionGuide.ts index 6bf9d194a..e84e457b8 100644 --- a/packages/service/core/ai/functions/createQuestionGuide.ts +++ b/packages/service/core/ai/functions/createQuestionGuide.ts @@ -1,5 +1,6 @@ import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d'; import { getAIApi } from '../config'; +import { countGptMessagesChars } from '../../chat/utils'; export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`; @@ -10,6 +11,13 @@ export async function createQuestionGuide({ messages: ChatMessageItemType[]; model: string; }) { + const concatMessages: ChatMessageItemType[] = [ + ...messages, + { + role: 'user', + content: Prompt_QuestionGuide + } + ]; const ai = getAIApi({ timeout: 480000 }); @@ -17,28 +25,21 @@ export async function createQuestionGuide({ model: model, temperature: 0.1, max_tokens: 200, - messages: [ - ...messages, - { - role: 'user', - content: Prompt_QuestionGuide - } - ], + messages: concatMessages, stream: false }); const answer = data.choices?.[0]?.message?.content || ''; - const inputTokens = data.usage?.prompt_tokens || 0; - const outputTokens = data.usage?.completion_tokens || 0; const start = answer.indexOf('['); const end = answer.lastIndexOf(']'); + const charsLength = countGptMessagesChars(concatMessages); + if (start === -1 || end === -1) { return { result: [], - inputTokens, - outputTokens + charsLength: 0 }; } @@ -50,14 +51,12 @@ export async function createQuestionGuide({ try { return { result: JSON.parse(jsonStr), - inputTokens, - outputTokens + charsLength }; } catch (error) { return { result: [], - inputTokens, - outputTokens + charsLength: 0 }; } } diff --git a/packages/service/core/ai/functions/queryExtension.ts b/packages/service/core/ai/functions/queryExtension.ts index a72820c62..48c5a50b8 100644 --- a/packages/service/core/ai/functions/queryExtension.ts +++ b/packages/service/core/ai/functions/queryExtension.ts @@ -1,18 +1,19 @@ import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { getAIApi } from '../config'; import { ChatItemType } from '@fastgpt/global/core/chat/type'; +import { countGptMessagesChars } from '../../chat/utils'; /* query extension - 问题扩展 可以根据上下文,消除指代性问题以及扩展问题,利于检索。 */ -const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确。例如: +const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确,并与原问题语言相同。例如: 历史记录: """ """ 原问题: 介绍下剧情。 -检索词: ["发生了什么故事?","故事梗概是什么?","讲述了什么故事?"] +检索词: ["介绍下故事的背景和主要人物。","故事的主题是什么?","剧情是是如何发展的?"] ---------------- 历史记录: """ @@ -20,7 +21,7 @@ Q: 对话背景。 A: 当前对话是关于 FatGPT 的介绍和使用等。 """ 原问题: 怎么下载 -检索词: ["FastGPT 怎么下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] +检索词: ["FastGPT 如何下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] ---------------- 历史记录: """ @@ -30,15 +31,15 @@ Q: 报错 "no connection" A: 报错"no connection"可能是因为…… """ 原问题: 怎么解决 -检索词: ["FastGPT 报错"no connection"如何解决?", "报错 'no connection' 是什么原因?", "FastGPT提示'no connection',要怎么办?"] +检索词: ["FastGPT 报错"no connection"如何解决?", "造成 'no connection' 报错的原因。", "FastGPT提示'no connection',要怎么办?"] ---------------- 历史记录: """ Q: 作者是谁? A: FastGPT 的作者是 labring。 """ -原问题: 介绍下他 -检索词: ["介绍下 FastGPT 的作者 labring。","作者 labring 的背景信息。","labring 为什么要做 FastGPT?"] +原问题: Tell me about him +检索词: ["Introduce labring, the author of FastGPT." ," Background information on author labring." "," Why does labring do FastGPT?"] ---------------- 历史记录: """ @@ -105,8 +106,7 @@ export const queryExtension = async ({ rawQuery: string; extensionQueries: string[]; model: string; - inputTokens: number; - outputTokens: number; + charsLength: number; }> => { const systemFewShot = chatBg ? `Q: 对话背景。 @@ -125,18 +125,20 @@ A: ${chatBg} timeout: 480000 }); + const messages = [ + { + role: 'user', + content: replaceVariable(defaultPrompt, { + query: `${query}`, + histories: concatFewShot + }) + } + ]; const result = await ai.chat.completions.create({ model: model, temperature: 0.01, - messages: [ - { - role: 'user', - content: replaceVariable(defaultPrompt, { - query: `${query}`, - histories: concatFewShot - }) - } - ], + // @ts-ignore + messages, stream: false }); @@ -146,8 +148,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - inputTokens: 0, - outputTokens: 0 + charsLength: 0 }; } @@ -160,8 +161,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: queries, model, - inputTokens: result.usage?.prompt_tokens || 0, - outputTokens: result.usage?.completion_tokens || 0 + charsLength: countGptMessagesChars(messages) }; } catch (error) { console.log(error); @@ -169,8 +169,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - inputTokens: 0, - outputTokens: 0 + charsLength: 0 }; } }; diff --git a/packages/service/core/app/schema.ts b/packages/service/core/app/schema.ts index 551e487e0..0dba217b7 100644 --- a/packages/service/core/app/schema.ts +++ b/packages/service/core/app/schema.ts @@ -61,6 +61,9 @@ const AppSchema = new Schema({ type: String, enum: Object.keys(PermissionTypeMap), default: PermissionTypeEnum.private + }, + teamTags: { + type: [String] } }); diff --git a/packages/service/core/chat/chatItemSchema.ts b/packages/service/core/chat/chatItemSchema.ts index 1a9e3b9f2..cd1919af8 100644 --- a/packages/service/core/chat/chatItemSchema.ts +++ b/packages/service/core/chat/chatItemSchema.ts @@ -92,6 +92,8 @@ try { ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true }); // admin charts ChatItemSchema.index({ time: -1, obj: 1 }, { background: true }); + // timer, clear history + ChatItemSchema.index({ teamId: 1, time: -1 }, { background: true }); } catch (error) { console.log(error); } diff --git a/packages/service/core/chat/chatSchema.ts b/packages/service/core/chat/chatSchema.ts index 727608e0c..5483b3e88 100644 --- a/packages/service/core/chat/chatSchema.ts +++ b/packages/service/core/chat/chatSchema.ts @@ -83,6 +83,9 @@ try { ChatSchema.index({ teamId: 1, appId: 1, updateTime: -1 }, { background: true }); // get share chat history ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1, source: 1 }, { background: true }); + + // timer, clear history + ChatSchema.index({ teamId: 1, updateTime: -1 }, { background: true }); } catch (error) { console.log(error); } diff --git a/packages/service/core/chat/utils.ts b/packages/service/core/chat/utils.ts index 91886749f..769be3f47 100644 --- a/packages/service/core/chat/utils.ts +++ b/packages/service/core/chat/utils.ts @@ -2,7 +2,10 @@ import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import { ChatRoleEnum, IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants'; import { countMessagesTokens, countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { adaptRole_Chat2Message } from '@fastgpt/global/core/chat/adapt'; -import type { ChatCompletionContentPart } from '@fastgpt/global/core/ai/type.d'; +import type { + ChatCompletionContentPart, + ChatMessageItemType +} from '@fastgpt/global/core/ai/type.d'; import axios from 'axios'; /* slice chat context by tokens */ @@ -56,6 +59,12 @@ export function ChatContextFilter({ return [...systemPrompts, ...chats]; } +export const countMessagesChars = (messages: ChatItemType[]) => { + return messages.reduce((sum, item) => sum + item.value.length, 0); +}; +export const countGptMessagesChars = (messages: ChatMessageItemType[]) => + messages.reduce((sum, item) => sum + item.content.length, 0); + /** string to vision model. Follow the markdown code block rule for interception: diff --git a/packages/service/core/dataset/collection/controller.ts b/packages/service/core/dataset/collection/controller.ts index 791531abd..9a5513050 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -147,8 +147,6 @@ export async function delCollectionAndRelatedSources({ collectionId: { $in: collectionIds } }); - await delay(2000); - // delete dataset.datas await MongoDatasetData.deleteMany({ teamId, collectionId: { $in: collectionIds } }, { session }); // delete imgs diff --git a/packages/service/core/dataset/controller.ts b/packages/service/core/dataset/controller.ts index 3ba222c61..d0779fbb3 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -66,6 +66,11 @@ export async function delDatasetRelevantData({ if (!datasets.length) return; const teamId = datasets[0].teamId; + + if (!teamId) { + return Promise.reject('teamId is required'); + } + const datasetIds = datasets.map((item) => String(item._id)); // Get _id, teamId, fileId, metadata.relatedImgId for all collections diff --git a/packages/service/core/dataset/data/schema.ts b/packages/service/core/dataset/data/schema.ts index 46442b159..35f5b05aa 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -7,10 +7,6 @@ import { } from '@fastgpt/global/support/user/team/constant'; import { DatasetCollectionName } from '../schema'; import { DatasetColCollectionName } from '../collection/schema'; -import { - DatasetDataIndexTypeEnum, - DatasetDataIndexTypeMap -} from '@fastgpt/global/core/dataset/constants'; export const DatasetDataCollectionName = 'dataset.datas'; @@ -54,11 +50,6 @@ const DatasetDataSchema = new Schema({ type: Boolean, default: false }, - type: { - type: String, - enum: Object.keys(DatasetDataIndexTypeMap), - default: DatasetDataIndexTypeEnum.custom - }, dataId: { type: String, required: true diff --git a/packages/service/core/dataset/search/utils.ts b/packages/service/core/dataset/search/utils.ts index 5689e5afb..4332423cf 100644 --- a/packages/service/core/dataset/search/utils.ts +++ b/packages/service/core/dataset/search/utils.ts @@ -14,22 +14,54 @@ export const datasetSearchQueryExtension = async ({ extensionBg?: string; histories?: ChatItemType[]; }) => { - // concat query - let queries = [query]; - let rewriteQuery = - histories.length > 0 - ? `${histories - .map((item) => { - return `${item.obj}: ${item.value}`; - }) - .join('\n')} - Human: ${query} - ` - : query; + const filterSamQuery = (queries: string[]) => { + const set = new Set(); + const filterSameQueries = queries.filter((item) => { + // 删除所有的标点符号与空格等,只对文本进行比较 + const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, '')); + if (set.has(str)) return false; + set.add(str); + return true; + }); + + return filterSameQueries; + }; + + let { queries, rewriteQuery, alreadyExtension } = (() => { + // concat query + let rewriteQuery = + histories.length > 0 + ? `${histories + .map((item) => { + return `${item.obj}: ${item.value}`; + }) + .join('\n')} + Human: ${query} + ` + : query; + + /* if query already extension, direct parse */ + try { + const jsonParse = JSON.parse(query); + const queries: string[] = Array.isArray(jsonParse) ? filterSamQuery(jsonParse) : [query]; + const alreadyExtension = Array.isArray(jsonParse); + return { + queries, + rewriteQuery: alreadyExtension ? queries.join('\n') : rewriteQuery, + alreadyExtension: alreadyExtension + }; + } catch (error) { + return { + queries: [query], + rewriteQuery, + alreadyExtension: false + }; + } + })(); // ai extension const aiExtensionResult = await (async () => { - if (!extensionModel) return; + if (!extensionModel || alreadyExtension) return; const result = await queryExtension({ chatBg: extensionBg, query, @@ -39,23 +71,13 @@ export const datasetSearchQueryExtension = async ({ if (result.extensionQueries?.length === 0) return; return result; })(); - if (aiExtensionResult) { - queries = queries.concat(aiExtensionResult.extensionQueries); + queries = filterSamQuery(queries.concat(aiExtensionResult.extensionQueries)); rewriteQuery = queries.join('\n'); } - const set = new Set(); - const filterSameQueries = queries.filter((item) => { - // 删除所有的标点符号与空格等,只对文本进行比较 - const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, '')); - if (set.has(str)) return false; - set.add(str); - return true; - }); - return { - concatQueries: filterSameQueries, + concatQueries: queries, rewriteQuery, aiExtensionResult }; diff --git a/packages/service/core/dataset/training/controller.ts b/packages/service/core/dataset/training/controller.ts index 382ec9d93..90eefe043 100644 --- a/packages/service/core/dataset/training/controller.ts +++ b/packages/service/core/dataset/training/controller.ts @@ -57,7 +57,7 @@ export async function pushDataListToTrainingQueue({ if (trainingMode === TrainingModeEnum.chunk) { const vectorModelData = vectorModelList?.find((item) => item.model === vectorModel); if (!vectorModelData) { - return Promise.reject(`Model ${vectorModel} is inValid`); + return Promise.reject(`File model ${vectorModel} is inValid`); } return { @@ -70,7 +70,7 @@ export async function pushDataListToTrainingQueue({ if (trainingMode === TrainingModeEnum.qa) { const qaModelData = datasetModelList?.find((item) => item.model === agentModel); if (!qaModelData) { - return Promise.reject(`Model ${agentModel} is inValid`); + return Promise.reject(`Vector model ${agentModel} is inValid`); } return { maxToken: qaModelData.maxContext * 0.8, diff --git a/packages/service/core/dataset/training/schema.ts b/packages/service/core/dataset/training/schema.ts index e9c746563..66662da88 100644 --- a/packages/service/core/dataset/training/schema.ts +++ b/packages/service/core/dataset/training/schema.ts @@ -2,7 +2,7 @@ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type'; -import { DatasetDataIndexTypeMap, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; +import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; import { DatasetColCollectionName } from '../collection/schema'; import { DatasetCollectionName } from '../schema'; import { @@ -86,11 +86,6 @@ const TrainingDataSchema = new Schema({ indexes: { type: [ { - type: { - type: String, - enum: Object.keys(DatasetDataIndexTypeMap), - required: true - }, text: { type: String, required: true diff --git a/packages/service/package.json b/packages/service/package.json index aff31afd5..8b85872f3 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -15,7 +15,7 @@ "nextjs-cors": "^2.1.2", "node-cron": "^3.0.3", "pg": "^8.10.0", - "date-fns": "^2.30.0", + "date-fns": "2.30.0", "tunnel": "^0.0.6" }, "devDependencies": { diff --git a/packages/service/support/openapi/auth.ts b/packages/service/support/openapi/auth.ts index cfce33e3d..a66c684b7 100644 --- a/packages/service/support/openapi/auth.ts +++ b/packages/service/support/openapi/auth.ts @@ -19,14 +19,15 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) { // auth limit // @ts-ignore if (global.feConfigs?.isPlus) { - await POST('/support/openapi/authLimit', { openApi } as AuthOpenApiLimitProps); + await POST('/support/openapi/authLimit', { + openApi: openApi.toObject() + } as AuthOpenApiLimitProps); } updateApiKeyUsedTime(openApi._id); return { apikey, - userId: String(openApi.userId), teamId: String(openApi.teamId), tmbId: String(openApi.tmbId), appId: openApi.appId || '' diff --git a/packages/service/support/openapi/schema.ts b/packages/service/support/openapi/schema.ts index 6eca9c95f..dff9af5a4 100644 --- a/packages/service/support/openapi/schema.ts +++ b/packages/service/support/openapi/schema.ts @@ -1,8 +1,6 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type'; -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { TeamCollectionName, TeamMemberCollectionName @@ -10,10 +8,6 @@ import { const OpenApiSchema = new Schema( { - userId: { - type: Schema.Types.ObjectId, - ref: 'user' - }, teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -44,22 +38,17 @@ const OpenApiSchema = new Schema( type: String, default: 'Api Key' }, - usage: { - // total usage. value from bill total + usagePoints: { type: Number, - default: 0, - get: (val: number) => formatStorePrice2Read(val) + default: 0 }, limit: { expiredTime: { type: Date }, - credit: { - // value from user settings + maxUsagePoints: { type: Number, - default: -1, - set: (val: number) => val * PRICE_SCALE, - get: (val: number) => formatStorePrice2Read(val) + default: -1 } } }, diff --git a/packages/service/support/openapi/tools.ts b/packages/service/support/openapi/tools.ts index b6bedee1e..d9e0946e8 100644 --- a/packages/service/support/openapi/tools.ts +++ b/packages/service/support/openapi/tools.ts @@ -8,15 +8,21 @@ export function updateApiKeyUsedTime(id: string) { }); } -export function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) { +export function updateApiKeyUsage({ + apikey, + totalPoints +}: { + apikey: string; + totalPoints: number; +}) { MongoOpenApi.findOneAndUpdate( { apiKey: apikey }, { $inc: { - usage + usagePoints: totalPoints } } ).catch((err) => { - console.log('update apiKey usage error', err); + console.log('update apiKey totalPoints error', err); }); } diff --git a/packages/service/support/outLink/schema.ts b/packages/service/support/outLink/schema.ts index acb7526ab..3d6d18482 100644 --- a/packages/service/support/outLink/schema.ts +++ b/packages/service/support/outLink/schema.ts @@ -35,8 +35,7 @@ const OutLinkSchema = new Schema({ type: String, required: true }, - total: { - // total amount + usagePoints: { type: Number, default: 0 }, @@ -48,6 +47,10 @@ const OutLinkSchema = new Schema({ default: false }, limit: { + maxUsagePoints: { + type: Number, + default: -1 + }, expiredTime: { type: Date }, @@ -55,16 +58,18 @@ const OutLinkSchema = new Schema({ type: Number, default: 1000 }, - credit: { - type: Number, - default: -1 - }, hookUrl: { type: String } } }); +try { + OutLinkSchema.index({ shareId: -1 }); +} catch (error) { + console.log(error); +} + export const MongoOutLink: Model = models['outlinks'] || model('outlinks', OutLinkSchema); diff --git a/packages/service/support/outLink/tools.ts b/packages/service/support/outLink/tools.ts index 3b9afa32d..878234c63 100644 --- a/packages/service/support/outLink/tools.ts +++ b/packages/service/support/outLink/tools.ts @@ -1,18 +1,19 @@ import axios from 'axios'; import { MongoOutLink } from './schema'; import { FastGPTProUrl } from '../../common/system/constants'; +import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; -export const updateOutLinkUsage = async ({ +export const addOutLinkUsage = async ({ shareId, - total + totalPoints }: { shareId: string; - total: number; + totalPoints: number; }) => { MongoOutLink.findOneAndUpdate( { shareId }, { - $inc: { total }, + $inc: { usagePoints: totalPoints }, lastTime: new Date() } ).catch((err) => { @@ -23,11 +24,13 @@ export const updateOutLinkUsage = async ({ export const pushResult2Remote = async ({ outLinkUid, shareId, + appName, responseData }: { outLinkUid?: string; // raw id, not parse shareId?: string; - responseData?: any[]; + appName: string; + responseData?: ChatHistoryItemResType[]; }) => { if (!shareId || !outLinkUid || !FastGPTProUrl) return; try { @@ -42,6 +45,7 @@ export const pushResult2Remote = async ({ url: '/shareAuth/finish', data: { token: outLinkUid, + appName, responseData } }); diff --git a/packages/service/support/permission/auth/dataset.ts b/packages/service/support/permission/auth/dataset.ts index c793714eb..e55ed064d 100644 --- a/packages/service/support/permission/auth/dataset.ts +++ b/packages/service/support/permission/auth/dataset.ts @@ -107,7 +107,7 @@ export async function authDatasetCollection({ collection: CollectionWithDatasetType; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const { role } = await getTmbInfoByTmbId({ tmbId }); const { collection, isOwner, canWrite } = await (async () => { @@ -143,7 +143,6 @@ export async function authDatasetCollection({ })(); return { - userId, teamId, tmbId, collection, @@ -163,7 +162,7 @@ export async function authDatasetFile({ file: DatasetFileSchema; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const [file, collection] = await Promise.all([ getFileById({ bucketName: BucketNameEnum.dataset, fileId }), @@ -190,7 +189,6 @@ export async function authDatasetFile({ }); return { - userId, teamId, tmbId, file, @@ -200,4 +198,4 @@ export async function authDatasetFile({ } catch (error) { return Promise.reject(DatasetErrEnum.unAuthDatasetFile); } -} +} \ No newline at end of file diff --git a/packages/service/support/permission/auth/user.ts b/packages/service/support/permission/auth/user.ts index e8da32072..1eb506f3f 100644 --- a/packages/service/support/permission/auth/user.ts +++ b/packages/service/support/permission/auth/user.ts @@ -12,7 +12,7 @@ export async function authUserNotVisitor(props: AuthModeType): Promise< role: `${TeamMemberRoleEnum}`; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const team = await getTmbInfoByTmbId({ tmbId }); if (team.role === TeamMemberRoleEnum.visitor) { @@ -20,7 +20,6 @@ export async function authUserNotVisitor(props: AuthModeType): Promise< } return { - userId, teamId, tmbId, team, diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index 56afce1ac..fb73b7107 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -94,10 +94,10 @@ export async function parseHeaderCert({ })(); // auth apikey - const { userId, teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); + const { teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); return { - uid: userId, + uid: '', teamId, tmbId, apikey, @@ -217,4 +217,4 @@ export const authFileToken = (token?: string) => fileId: decoded.fileId }); }); - }); + }); \ No newline at end of file diff --git a/packages/service/support/permission/limit/dataset.ts b/packages/service/support/permission/limit/dataset.ts deleted file mode 100644 index b6e8a18c9..000000000 --- a/packages/service/support/permission/limit/dataset.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type'; -import { getVectorCountByTeamId } from '../../../common/vectorStore/controller'; -import { getTeamDatasetMaxSize } from '../../wallet/sub/utils'; - -export const checkDatasetLimit = async ({ - teamId, - insertLen = 0, - standardPlans -}: { - teamId: string; - insertLen?: number; - standardPlans?: StandSubPlanLevelMapType; -}) => { - const [{ maxSize }, usedSize] = await Promise.all([ - getTeamDatasetMaxSize({ teamId, standardPlans }), - getVectorCountByTeamId(teamId) - ]); - - if (usedSize + insertLen >= maxSize) { - return Promise.reject(`数据库容量不足,无法继续添加。可以在账号页面进行扩容。`); - } - return; -}; diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index 361a9029b..5765df3a7 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -2,7 +2,6 @@ import { UserType } from '@fastgpt/global/support/user/type'; import { MongoUser } from './schema'; import { getTmbInfoByTmbId, getUserDefaultTeam } from './team/controller'; import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode'; -import { UserErrEnum } from '@fastgpt/global/common/error/code/user'; export async function authUserExist({ userId, username }: { userId?: string; username?: string }) { if (userId) { @@ -47,22 +46,3 @@ export async function getUserDetail({ team: tmb }; } - -export async function getUserAndAuthBalance({ - tmbId, - minBalance -}: { - tmbId: string; - minBalance?: number; -}) { - const user = await getUserDetail({ tmbId }); - - if (!user) { - return Promise.reject(UserErrEnum.unAuthUser); - } - if (minBalance !== undefined && user.team.balance < minBalance) { - return Promise.reject(UserErrEnum.balanceNotEnough); - } - - return user; -} diff --git a/packages/service/support/user/schema.ts b/packages/service/support/user/schema.ts index 15e75c491..496726860 100644 --- a/packages/service/support/user/schema.ts +++ b/packages/service/support/user/schema.ts @@ -1,7 +1,7 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants'; +import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; @@ -63,6 +63,8 @@ const UserSchema = new Schema({ }); try { + // login + UserSchema.index({ username: 1, password: 1 }); UserSchema.index({ createTime: -1 }); } catch (error) { console.log(error); diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 042cd2388..17e27ab5f 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -36,7 +36,7 @@ export async function getTmbInfoByTmbId({ tmbId }: { tmbId: string }) { return Promise.reject('tmbId or userId is required'); } return getTeamMember({ - _id: new Types.ObjectId(tmbId), + _id: new Types.ObjectId(String(tmbId)), status: notLeaveStatus }); } diff --git a/packages/service/support/user/team/teamSchema.ts b/packages/service/support/user/team/teamSchema.ts index 3121b92ae..5a358b409 100644 --- a/packages/service/support/user/team/teamSchema.ts +++ b/packages/service/support/user/team/teamSchema.ts @@ -27,7 +27,10 @@ const TeamSchema = new Schema({ }, maxSize: { type: Number, - default: 3 + default: 1 + }, + tagsUrl: { + type: String }, limit: { lastExportDatasetTime: { diff --git a/packages/service/support/user/team/teamTagsSchema.ts b/packages/service/support/user/team/teamTagsSchema.ts new file mode 100644 index 000000000..f1c1044e6 --- /dev/null +++ b/packages/service/support/user/team/teamTagsSchema.ts @@ -0,0 +1,35 @@ +import { connectionMongo, type Model } from '../../../common/mongo'; +const { Schema, model, models } = connectionMongo; +import { TeamTagsSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d'; +import { + TeamCollectionName, + TeamTagsCollectionName +} from '@fastgpt/global/support/user/team/constant'; + +const TeamTagsSchema = new Schema({ + label: { + type: String, + required: true + }, + teamId: { + type: Schema.Types.ObjectId, + ref: TeamCollectionName, + required: true + }, + key: { + type: String + }, + createTime: { + type: Date, + default: () => new Date() + } +}); + +try { + TeamTagsSchema.index({ teamId: 1 }); +} catch (error) { + console.log(error); +} + +export const MongoTeamTags: Model = + models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagsSchema); diff --git a/packages/service/support/wallet/sub/schema.ts b/packages/service/support/wallet/sub/schema.ts index 2d2258070..11316878e 100644 --- a/packages/service/support/wallet/sub/schema.ts +++ b/packages/service/support/wallet/sub/schema.ts @@ -1,3 +1,8 @@ +/* + user sub plan + 1. type=standard: There will only be 1, and each team will have one + 2. type=extraDatasetSize/extraPoints: Can buy multiple +*/ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; @@ -23,25 +28,10 @@ const SubSchema = new Schema({ required: true }, status: { - // active: continue sub; canceled: canceled sub; type: String, enum: Object.keys(subStatusMap), required: true }, - mode: { - type: String, - enum: Object.keys(subModeMap) - }, - currentMode: { - type: String, - enum: Object.keys(subModeMap), - required: true - }, - nextMode: { - type: String, - enum: Object.keys(subModeMap), - required: true - }, startTime: { type: Date, default: () => new Date() @@ -55,12 +45,16 @@ const SubSchema = new Schema({ type: Number, required: true }, - pointPrice: { - // stand level point total price - type: Number - }, - // sub content + // standard sub + currentMode: { + type: String, + enum: Object.keys(subModeMap) + }, + nextMode: { + type: String, + enum: Object.keys(subModeMap) + }, currentSubLevel: { type: String, enum: Object.keys(standardSubLevelMap) @@ -69,79 +63,34 @@ const SubSchema = new Schema({ type: String, enum: Object.keys(standardSubLevelMap) }, + + // stand sub and extra points sub. Plan total points totalPoints: { type: Number }, + pointPrice: { + // stand level point total price + type: Number + }, + surplusPoints: { + // plan surplus points + type: Number + }, + // extra dataset size currentExtraDatasetSize: { type: Number - }, - nextExtraDatasetSize: { - type: Number - }, - - currentExtraPoints: { - type: Number - }, - nextExtraPoints: { - type: Number - }, - - // standard sub limit - // maxTeamMember: { - // type: Number - // }, - // maxAppAmount: { - // type: Number - // }, - // maxDatasetAmount: { - // type: Number - // }, - // chatHistoryStoreDuration: { - // // n day - // type: Number - // }, - // maxDatasetSize: { - // type: Number - // }, - // trainingWeight: { - // // 0 1 2 3 - // type: Number - // }, - // customApiKey: { - // type: Boolean - // }, - // customCopyright: { - // type: Boolean - // }, - // websiteSyncInterval: { - // // hours - // type: Number - // }, - // reRankWeight: { - // // 0 1 2 3 - // type: Number - // }, - // totalPoints: { - // // record standard sub points - // type: Number - // }, - - surplusPoints: { - // standard sub / extra points sub - type: Number - }, - - // abandon - renew: Boolean, //决定是否续费 - datasetStoreAmount: Number + } }); try { - SubSchema.index({ teamId: 1 }); - SubSchema.index({ status: 1 }); - SubSchema.index({ type: 1 }); - SubSchema.index({ expiredTime: -1 }); + // get team plan + SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 }); + + // timer task. check expired plan; update standard plan; + SubSchema.index({ type: 1, expiredTime: -1 }); + // timer task. clear dead team + SubSchema.index({ type: 1, currentSubLevel: 1, nextSubLevel: 1 }); } catch (error) { console.log(error); } diff --git a/packages/service/support/wallet/sub/utils.ts b/packages/service/support/wallet/sub/utils.ts index feb29c8ff..760ad6716 100644 --- a/packages/service/support/wallet/sub/utils.ts +++ b/packages/service/support/wallet/sub/utils.ts @@ -1,87 +1,70 @@ import { SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants'; import { MongoTeamSub } from './schema'; -import { addHours } from 'date-fns'; -import { FeTeamSubType, StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type.d'; +import { + FeTeamPlanStatusType, + StandSubPlanLevelMapType +} from '@fastgpt/global/support/wallet/sub/type.d'; import { getVectorCountByTeamId } from '../../../common/vectorStore/controller'; -/* get team dataset max size */ -export const getTeamDatasetMaxSize = async ({ +export const getTeamSubPlans = async ({ teamId, standardPlans }: { teamId: string; standardPlans?: StandSubPlanLevelMapType; -}) => { - if (!standardPlans) { - return { - maxSize: Infinity, - sub: null - }; - } - - const plans = await MongoTeamSub.find({ - teamId, - expiredTime: { $gte: addHours(new Date(), -3) } - }).lean(); - - const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); - const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize); - - const standardMaxDatasetSize = - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity - : Infinity; - const totalDatasetSize = - standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0); - - return { - maxSize: totalDatasetSize, - sub: extraDatasetSize - }; -}; - -export const getTeamSubPlanStatus = async ({ - teamId, - standardPlans -}: { - teamId: string; - standardPlans?: StandSubPlanLevelMapType; -}): Promise => { +}): Promise => { const [plans, usedDatasetSize] = await Promise.all([ MongoTeamSub.find({ teamId }).lean(), getVectorCountByTeamId(teamId) ]); const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); - const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize); - const extraPoints = plans.find((plan) => plan.type === SubTypeEnum.extraPoints); + const extraDatasetSize = plans.filter((plan) => plan.type === SubTypeEnum.extraDatasetSize); + const extraPoints = plans.filter((plan) => plan.type === SubTypeEnum.extraPoints); + + const totalPoints = + (standard?.totalPoints || 0) + + extraPoints.reduce((acc, cur) => acc + (cur.totalPoints || 0), 0); + const surplusPoints = + (standard?.surplusPoints || 0) + + extraPoints.reduce((acc, cur) => acc + (cur.surplusPoints || 0), 0); const standardMaxDatasetSize = standard?.currentSubLevel && standardPlans ? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity : Infinity; const totalDatasetSize = - standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0); - - const standardMaxPoints = - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel]?.totalPoints || Infinity - : Infinity; - const totalPoints = standardMaxPoints + (extraPoints?.currentExtraPoints || 0); - - const surplusPoints = (standard?.surplusPoints || 0) + (extraPoints?.surplusPoints || 0); + standardMaxDatasetSize + + extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0); return { [SubTypeEnum.standard]: standard, - [SubTypeEnum.extraDatasetSize]: extraDatasetSize, - [SubTypeEnum.extraPoints]: extraPoints, + standardConstants: + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel] + : undefined, - standardMaxDatasetSize, - datasetMaxSize: totalDatasetSize, - usedDatasetSize, - - standardMaxPoints, totalPoints, - usedPoints: totalPoints - surplusPoints + usedPoints: totalPoints - surplusPoints, + + datasetMaxSize: totalDatasetSize, + usedDatasetSize + }; +}; +export const getTeamStandPlan = async ({ + teamId, + standardPlans +}: { + teamId: string; + standardPlans?: StandSubPlanLevelMapType; +}) => { + const standard = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }).lean(); + + return { + [SubTypeEnum.standard]: standard, + standardConstants: + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel] + : undefined }; }; diff --git a/packages/service/support/wallet/bill/controller.ts b/packages/service/support/wallet/usage/controller.ts similarity index 62% rename from packages/service/support/wallet/bill/controller.ts rename to packages/service/support/wallet/usage/controller.ts index 3528159a0..43e9534ae 100644 --- a/packages/service/support/wallet/bill/controller.ts +++ b/packages/service/support/wallet/usage/controller.ts @@ -1,8 +1,8 @@ -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; -import { MongoBill } from './schema'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { MongoUsage } from './schema'; import { ClientSession } from '../../../common/mongo'; -export const createTrainingBill = async ({ +export const createTrainingUsage = async ({ teamId, tmbId, appName, @@ -14,33 +14,33 @@ export const createTrainingBill = async ({ teamId: string; tmbId: string; appName: string; - billSource: `${BillSourceEnum}`; + billSource: `${UsageSourceEnum}`; vectorModel: string; agentModel: string; session?: ClientSession; }) => { - const [{ _id }] = await MongoBill.create( + const [{ _id }] = await MongoUsage.create( [ { teamId, tmbId, appName, source: billSource, + totalPoints: 0, list: [ { - moduleName: 'wallet.moduleName.index', + moduleName: 'support.wallet.moduleName.index', model: vectorModel, charsLength: 0, amount: 0 }, { - moduleName: 'wallet.moduleName.qa', + moduleName: 'support.wallet.moduleName.qa', model: agentModel, charsLength: 0, amount: 0 } - ], - total: 0 + ] } ], { session } diff --git a/packages/service/support/wallet/bill/schema.ts b/packages/service/support/wallet/usage/schema.ts similarity index 50% rename from packages/service/support/wallet/bill/schema.ts rename to packages/service/support/wallet/usage/schema.ts index c4d7bef38..e6c39355d 100644 --- a/packages/service/support/wallet/bill/schema.ts +++ b/packages/service/support/wallet/usage/schema.ts @@ -1,13 +1,15 @@ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { BillSchema as BillType } from '@fastgpt/global/support/wallet/bill/type'; -import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; +import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type'; +import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; import { TeamCollectionName, TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; -const BillSchema = new Schema({ +export const UsageCollectionName = 'usages'; + +const UsageSchema = new Schema({ teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -18,6 +20,11 @@ const BillSchema = new Schema({ ref: TeamMemberCollectionName, required: true }, + source: { + type: String, + enum: Object.keys(UsageSourceMap), + required: true + }, appName: { type: String, default: '' @@ -31,16 +38,16 @@ const BillSchema = new Schema({ type: Date, default: () => new Date() }, - total: { - // 1 * PRICE_SCALE + totalPoints: { + // total points type: Number, required: true }, - source: { - type: String, - enum: Object.keys(BillSourceMap), - required: true - }, + // total: { + // // total points + // type: Number, + // required: true + // }, list: { type: Array, default: [] @@ -48,11 +55,15 @@ const BillSchema = new Schema({ }); try { - BillSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true }); - BillSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 }); + UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true }); + // timer task. clear dead team + UsageSchema.index({ teamId: 1, time: -1 }, { background: true }); + + UsageSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 }); } catch (error) { console.log(error); } -export const MongoBill: Model = models['bill'] || model('bill', BillSchema); -MongoBill.syncIndexes(); +export const MongoUsage: Model = + models[UsageCollectionName] || model(UsageCollectionName, UsageSchema); +MongoUsage.syncIndexes(); diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 4642d8e38..09ae20777 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -5,6 +5,13 @@ export const iconPaths = { closeSolid: () => import('./icons/closeSolid.svg'), collectionLight: () => import('./icons/collectionLight.svg'), collectionSolid: () => import('./icons/collectionSolid.svg'), + 'acount/cube': () => import('./icons/acount/cube.svg'), + 'acount/personalized': () => import('./icons/acount/personalized.svg'), + 'acount/user': () => import('./icons/acount/user.svg'), + 'acount/plans': () => import('./icons/acount/plans.svg'), + 'acount/check': () => import('./icons/acount/check.svg'), + 'acount/arrowRight': () => import('./icons/acount/arrowRight.svg'), + 'acount/plansBlue': () => import('./icons/acount/plansBlue.svg'), 'common/addCircleLight': () => import('./icons/common/addCircleLight.svg'), 'common/backFill': () => import('./icons/common/backFill.svg'), 'common/backLight': () => import('./icons/common/backLight.svg'), @@ -128,6 +135,7 @@ export const iconPaths = { kbTest: () => import('./icons/kbTest.svg'), menu: () => import('./icons/menu.svg'), minus: () => import('./icons/minus.svg'), + 'modal/confirmPay': () => import('./icons/modal/confirmPay.svg'), 'modal/edit': () => import('./icons/modal/edit.svg'), 'modal/manualDataset': () => import('./icons/modal/manualDataset.svg'), 'modal/selectSource': () => import('./icons/modal/selectSource.svg'), @@ -143,19 +151,20 @@ export const iconPaths = { stop: () => import('./icons/stop.svg'), 'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'), 'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'), - 'support/bill/billRecordLight': () => import('./icons/support/bill/billRecordLight.svg'), + 'support/bill/extraDatasetsize': () => import('./icons/support/bill/extraDatasetsize.svg'), + 'support/bill/extraPoints': () => import('./icons/support/bill/extraPoints.svg'), + 'support/bill/payRecordLight': () => import('./icons/support/bill/payRecordLight.svg'), + 'support/bill/priceLight': () => import('./icons/support/bill/priceLight.svg'), + 'support/bill/shoppingCart': () => import('./icons/support/bill/shoppingCart.svg'), 'support/outlink/apikeyFill': () => import('./icons/support/outlink/apikeyFill.svg'), 'support/outlink/apikeyLight': () => import('./icons/support/outlink/apikeyLight.svg'), 'support/outlink/iframeLight': () => import('./icons/support/outlink/iframeLight.svg'), 'support/outlink/share': () => import('./icons/support/outlink/share.svg'), 'support/outlink/shareLight': () => import('./icons/support/outlink/shareLight.svg'), - 'support/pay/extraDatasetsize': () => import('./icons/support/pay/extraDatasetsize.svg'), - 'support/pay/extraPoints': () => import('./icons/support/pay/extraPoints.svg'), - 'support/pay/payRecordLight': () => import('./icons/support/pay/payRecordLight.svg'), - 'support/pay/priceLight': () => import('./icons/support/pay/priceLight.svg'), 'support/permission/privateLight': () => import('./icons/support/permission/privateLight.svg'), 'support/permission/publicLight': () => import('./icons/support/permission/publicLight.svg'), 'support/team/memberLight': () => import('./icons/support/team/memberLight.svg'), + 'support/usage/usageRecordLight': () => import('./icons/support/usage/usageRecordLight.svg'), 'support/user/informLight': () => import('./icons/support/user/informLight.svg'), 'support/user/userFill': () => import('./icons/support/user/userFill.svg'), 'support/user/userLight': () => import('./icons/support/user/userLight.svg'), diff --git a/packages/web/components/common/Icon/icons/acount/arrowRight.svg b/packages/web/components/common/Icon/icons/acount/arrowRight.svg new file mode 100644 index 000000000..461a2f530 --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/arrowRight.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/acount/check.svg b/packages/web/components/common/Icon/icons/acount/check.svg new file mode 100644 index 000000000..b063a333d --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/acount/cube.svg b/packages/web/components/common/Icon/icons/acount/cube.svg new file mode 100644 index 000000000..cab560dc5 --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/cube.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/acount/personalized.svg b/packages/web/components/common/Icon/icons/acount/personalized.svg new file mode 100644 index 000000000..8b9565b08 --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/personalized.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/acount/plans.svg b/packages/web/components/common/Icon/icons/acount/plans.svg new file mode 100644 index 000000000..e8aa85672 --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/plans.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web/components/common/Icon/icons/acount/plansBlue.svg b/packages/web/components/common/Icon/icons/acount/plansBlue.svg new file mode 100644 index 000000000..7d620cef6 --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/plansBlue.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web/components/common/Icon/icons/acount/user.svg b/packages/web/components/common/Icon/icons/acount/user.svg new file mode 100644 index 000000000..7c8faf3fd --- /dev/null +++ b/packages/web/components/common/Icon/icons/acount/user.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/components/common/Icon/icons/modal/confirmPay.svg b/packages/web/components/common/Icon/icons/modal/confirmPay.svg new file mode 100644 index 000000000..bc4f40f6e --- /dev/null +++ b/packages/web/components/common/Icon/icons/modal/confirmPay.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg b/packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg rename to packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/extraPoints.svg b/packages/web/components/common/Icon/icons/support/bill/extraPoints.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/extraPoints.svg rename to packages/web/components/common/Icon/icons/support/bill/extraPoints.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg b/packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg rename to packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/priceLight.svg b/packages/web/components/common/Icon/icons/support/bill/priceLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/priceLight.svg rename to packages/web/components/common/Icon/icons/support/bill/priceLight.svg diff --git a/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg b/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg new file mode 100644 index 000000000..c701b2576 --- /dev/null +++ b/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/bill/billRecordLight.svg b/packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/billRecordLight.svg rename to packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f094cca27..23807b50e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -88,7 +88,7 @@ importers: specifier: ^0.5.0 version: 0.5.0 date-fns: - specifier: ^2.30.0 + specifier: 2.30.0 version: 2.30.0 dayjs: specifier: ^1.11.7 @@ -285,7 +285,7 @@ importers: specifier: ^1.5.1 version: 1.6.7 date-fns: - specifier: ^2.30.0 + specifier: 2.30.0 version: 2.30.0 dayjs: specifier: ^1.11.7 @@ -3789,7 +3789,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] requiresBuild: true optional: true @@ -3798,7 +3797,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] requiresBuild: true optional: true @@ -3807,7 +3805,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] requiresBuild: true optional: true @@ -3816,7 +3813,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] requiresBuild: true optional: true @@ -3903,7 +3899,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] requiresBuild: true dev: false optional: true @@ -3913,7 +3908,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] requiresBuild: true dev: false optional: true @@ -3923,7 +3917,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] requiresBuild: true dev: false optional: true @@ -3933,7 +3926,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] requiresBuild: true dev: false optional: true @@ -7927,6 +7919,9 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -10685,10 +10680,13 @@ packages: /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + requiresBuild: true dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: false + optional: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} diff --git a/projects/app/data/config.json b/projects/app/data/config.json index 6eeeb82ee..21cc58981 100644 --- a/projects/app/data/config.json +++ b/projects/app/data/config.json @@ -13,8 +13,7 @@ "maxResponse": 4000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -32,8 +31,7 @@ "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -51,8 +49,7 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -70,10 +67,9 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, - "vision": false, + "vision": true, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -87,8 +83,7 @@ { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100 @@ -99,22 +94,44 @@ { "model": "tts-1", "name": "OpenAI TTS1", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "voices": [ - { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, - { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, - { "label": "Fable", "value": "fable", "bufferId": "openai-Fable" }, - { "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" }, - { "label": "Nova", "value": "nova", "bufferId": "openai-Nova" }, - { "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" } + { + "label": "Alloy", + "value": "alloy", + "bufferId": "openai-Alloy" + }, + { + "label": "Echo", + "value": "echo", + "bufferId": "openai-Echo" + }, + { + "label": "Fable", + "value": "fable", + "bufferId": "openai-Fable" + }, + { + "label": "Onyx", + "value": "onyx", + "bufferId": "openai-Onyx" + }, + { + "label": "Nova", + "value": "nova", + "bufferId": "openai-Nova" + }, + { + "label": "Shimmer", + "value": "shimmer", + "bufferId": "openai-Shimmer" + } ] } ], "whisperModel": { "model": "whisper-1", "name": "Whisper1", - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 } } diff --git a/projects/app/next.config.js b/projects/app/next.config.js index ad9a7d860..ef9310e53 100644 --- a/projects/app/next.config.js +++ b/projects/app/next.config.js @@ -45,13 +45,7 @@ const nextConfig = { }, transpilePackages: ['@fastgpt/*'], experimental: { - serverComponentsExternalPackages: [ - 'mongoose', - 'pg', - 'react', - '@chakra-ui/react', - '@lexical/react' - ], + serverComponentsExternalPackages: ['mongoose', 'pg'], outputFileTracingRoot: path.join(__dirname, '../../') } }; diff --git a/projects/app/package.json b/projects/app/package.json index 5358239a6..c50beb88e 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -26,7 +26,7 @@ "@tanstack/react-query": "^4.24.10", "@types/nprogress": "^0.2.0", "axios": "^1.5.1", - "date-fns": "^2.30.0", + "date-fns": "2.30.0", "dayjs": "^1.11.7", "echarts": "^5.4.1", "echarts-gl": "^2.0.9", @@ -74,4 +74,4 @@ "eslint-config-next": "13.1.6", "typescript": "4.9.5" } -} +} \ No newline at end of file diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index f740a75e2..5f1cd9eb7 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -2,7 +2,7 @@ 1. 新增 - 知识库搜索合并模块。 2. 新增 - 新的 Http 模块,支持更加灵活的参数传入。同时支持了输入输出自动数据类型转化,例如:接口输出的 JSON 类型会自动转成字符串类型,直接给其他模块使用。此外,还补充了一些例子,可在文档中查看。 -3. 优化 - 问题补全并入知识库搜索模块,无需单独配置。并且问题补全的同时,实现了问题扩展,丰富搜索的语义。(知识库模块会看到有2个参数配置,有一个是多余的,如果想让它消失,可以删除模块,重新增加一个) +3. 优化 - 问题优化并入知识库搜索模块,无需单独配置。并且问题优化的同时,实现了问题扩展,丰富搜索的语义。(知识库模块会看到有2个参数配置,有一个是多余的,如果想让它消失,可以删除模块,重新增加一个) 4. 修复 - 语音输入文件无法上传。 5. 修复 - 对话框重新生成无法使用。 6. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro) diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 22e3d2444..d2e12ef14 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -15,6 +15,7 @@ "AI Settings": "AI Settings", "Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left", "App Detail": "App Detail", + "Apps Share": "Apps Share", "Basic Settings": "Basic Settings", "Chat Debug": "Chat Debug", "Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations", @@ -83,6 +84,7 @@ "Delete Success": "Delete Successful", "Delete Tip": "Delete Confirm", "Delete Warning": "Warning", + "Detail": "Detail", "Done": "Done", "Edit": "Edit", "Exit": "Exit", @@ -98,8 +100,6 @@ "Last use time": "Last use time", "Load Failed": "Load Failed", "Loading": "Loading", - "Max credit": "Credit", - "Max credit tips": "What is the maximum amount of money that can be consumed by the link? If the link is exceeded, it will be banned. -1 indicates no limit.", "More settings": "More settings", "Name": "Name", "Name Can": "Name Can't Be Empty", @@ -110,6 +110,7 @@ "Number of words": "{{amount}} words", "OK": "OK", "Opened": "Opened", + "Other": "Other", "Output": "Output", "Params": "Params", "Password inconsistency": "Password inconsistency", @@ -132,17 +133,20 @@ "Select template": "Select template", "Set Avatar": "Set Avatar", "Set Name": "Make a nice name", + "Set Team Tags": "Set Team Tags", "Setting": "Setting", "Status": "Status", "Submit failed": "Submit failed", "Submit success": "Update Success", "Team": "Team", + "Team Tags Set": "Team Tags", "Test": "Test", "Time": "Time", "Un used": "Unused", "UnKnow": "UnKnow", "UnKnow Source": "UnKnow Source", "Unlimited": "Unlimited", + "Update": "Update", "Update Failed": "Update Failed", "Update Success": "Update Success", "Update Successful": "Update Successful", @@ -500,6 +504,7 @@ "Manual collection": "Manual collection", "My Dataset": "My Dataset", "Name": "Name", + "Query extension intro": "If the problem completion function is enabled, the accuracy of knowledge base search can be improved in continuous conversations. After this function is enabled, when searching the knowledge base, AI will be used to complete the missing information of the problem according to the conversation records.", "Quote Length": "Quote Length", "Read Dataset": "Read Dataset", "Search score tip": "{{scoreText}}Here are the rankings and scores:\n----\n{{detailScore}}", @@ -609,7 +614,8 @@ "Down load csv template": "Down load csv template", "Embedding Estimated Price Tips": "Index billing: {{price}}/1k chars", "Estimated Price": "Estimated Price: : {{amount}}{{unit}}", - "Estimated Price Tips": "QA charges\nInput: {{inputPrice}}/1k tokens\nOutput: {{outputPrice}}/1k tokens", + "Estimated Price Tips": "QA charges\nInput: 1k chars={{charsPointsPrice}} points", + "Estimated points": "About {{points}} points", "Fetch Error": "Get link failed", "Fetch Url": "Url", "Fetch url placeholder": "Up to 10 links, one per line.", @@ -679,7 +685,7 @@ "Source name": "Source", "Top K": "Top K", "Using cfr": "Open query extension", - "Using query extension": "", + "Using query extension": "Open query extension", "mode": { "embedding": "Vector recall", "embedding desc": "Use vectors for text correlation queries", @@ -843,8 +849,8 @@ "dynamicTargetInput": "dynamic Target Input", "input": "Input", "selectApp": "App Selector", - "selectChatModel": "Select Chat Model", "selectDataset": "Dataset Selector", + "selectLLMModel": "Select Chat Model", "switch": "Switch", "target": "Target Data", "textarea": "Textarea" @@ -856,6 +862,7 @@ "Ai response content": "Will be triggered after the stream reply is complete", "New context": "Concatenate the reply content with history and return it as a new context", "Quote": "Always return an array, if you want the search results to be empty to perform additional operations, you need to use the above two inputs and the trigger of the target module", + "query extension result": "Output as an array of strings to connect the result directly to the 'Question' of the knowledge base search", "running done": "Triggered when the module call finish" }, "label": { @@ -864,7 +871,7 @@ "Quote": "Quote", "Search result empty": "Search result empty", "Search result not empty": "Search result not empty", - "cfr result": "Response text", + "query extension result": "Response text", "result false": "False", "result true": "True", "running done": "done", @@ -1103,6 +1110,7 @@ "navbar": { "Account": "Account", "Apps": "App", + "Apps Share": "", "Chat": "Chat", "Datasets": "DataSet", "Module": "Module", @@ -1167,6 +1175,9 @@ "Usage": "Usage" }, "outlink": { + "Max usage points": "Max usage", + "Max usage points tip": "The maximum number of credits allowed for this link will not be used. -1 indicates no limit.", + "Usage points": "Usage points", "share": { "Response Quote": "Show Quote", "Response Quote tips": "The referenced content is returned in the share link, but the user is not allowed to download the original document." @@ -1175,9 +1186,16 @@ "subscription": { "Cancel subscription": "Cancel" }, + "team": { + "limit": { + "No permission rerank": "Not permission to rerank, please upgrade your plan" + } + }, "user": { + "Avatar": "Avatar", "Need to login": "Please log in first", "Price": "Price", + "User self info": "My info", "auth": { "Sending Code": "Sending" }, @@ -1192,7 +1210,10 @@ } }, "wallet": { + "Amount": "Amount", "Balance not enough tip": "The balance is insufficient, please go to the account page first", + "Bills": "Bill", + "Buy": "Buy", "Buy more": "Buy more", "Confirm pay": "Confirm pay", "Pay error": "Pay error", @@ -1200,32 +1221,60 @@ "bill": { "AI Model": "AI Model", "AI Type": "AI Type", - "Price": "Price(¥)" + "Number": "Number", + "Price": "Price(¥)", + "Status": "Status", + "Type": "Bill type", + "payWay": { + "Way": "Pay way", + "balance": "Balance", + "wx": "Wechat" + }, + "status": { + "closed": "CLOSED", + "notpay": "NOT_PAY", + "refund": "REFUND", + "success": "SUCCESS" + } }, + "moduleName": { + "index": "Index Generation", + "qa": "QA Generation" + }, + "noBill": "Not Bills", "subscription": { + "AI points": "AI points", "Ai points": "AI Points Standard", "Buy now": "Buy now", "Change will take effect after the current subscription expires": "Change will take effect after the current subscription expires", "Current dataset store": "Current dataset store subscription", + "Current extra ai points": "Current extra points", "Current plan": "Current plan", "Dataset store": "Dataset store size", "Dataset store price tip": "Deduct it from the account balance on the 1st of each month", "Expand size": "Expand size", + "Extra ai points": "Extra ai points", "Extra dataset size": "Extra dataset size", "Extra plan": "Extra Plan", "Extra plan tip": "When the standard plan is not enough, you can purchase an additional plan to continue using", "FAQ": "Pricing FAQs", + "Month amount": "Month", + "Next extra ai points": "Next extra points", "Next plan": "Future plan", "Next sub dataset size": "", "Nonsupport": "Nonsupport", "Refund plan and pay confirm": "There is no extra cost for you to switch this package and {{amount}} will be refunded to the balance.", + "Stand plan level": "Sub plan", "Standard plan pay confirm": "To switch this package, you need to pay {{payPrice}} Yuan.", "Standard update fail": "Update plan failed.", "Standard update success": "Change subscription plan successful!", "Sub plan": "Pricing Plans", "Sub plan tip": "Use FastGPT for free or upgrade to a higher plan", + "Team plan and usage": "Plan and usage", "Training weight": "Training weight: {{weight}}", - "Update extra dataset size": "Update size", + "Update extra ai points": "AI Points", + "Update extra dataset size": "Dataset size", + "Upgrade plan": "Upgrade plan", "function": { "History store": "", "Max app": "", @@ -1236,6 +1285,7 @@ }, "mode": { "Month": "Monthly", + "Period": "Period", "Year": "Yearly", "Year sale": "2 months free" }, @@ -1248,9 +1298,38 @@ "team": "" }, "type": { + "balance": "Add Balance", "extraDatasetSize": "Extra dataset size", - "standard": "" + "extraPoints": "AI Points", + "standard": "Sub plan" } + }, + "usage": { + "Ai model": "Ai Model", + "App name": "App name", + "Audio Speech": "Audio Speech", + "Bill Module": "Bill Detail", + "Chars length": "Chars length", + "Data Length": "Data length", + "Dataset store": "Dataset store", + "Duration": "Duration(s)", + "Extension Input Token Length": "Extension input tokens", + "Extension Output Token Length": "Extension output tokens", + "Extension result": "Extension result", + "Input Token Length": "Input tokens", + "Module name": "Module name", + "Number": "Bill ID", + "Output Token Length": "Output tokens", + "ReRank": "ReRank", + "Source": "Source", + "Text Length": "Text length", + "Time": "Time", + "Token Length": "Tokens", + "Total": "Total", + "Total points": "AI points usage", + "Usage Detail": "Usage Detail", + "Whisper": "Whisper", + "bill username": "User" } } }, @@ -1292,6 +1371,7 @@ "Set OpenAI Account Failed": "Set OpenAI account failed", "Sign Out": "Sign Out", "Source": "Source", + "Standard Detail": "", "Team": "Team", "Time": "Time", "Timezone": "Timezone", @@ -1339,7 +1419,11 @@ "Select Team": "Select Team", "Set Name": "Team Name", "Switch Team Failed": "Switch Team Failed", + "Tags Async": "Tag synchronization", "Team Name": "Team Name", + "Team Tags Async": "Team Tags Async", + "Team Tags Async Success": "Team Tags Async Success", + "Team Tags Async Tip": "Fill in the tag sync connection to get the latest", "Update Team": "Update Team", "invite": { "Accept Confirm": "Want to join the team?", @@ -1364,37 +1448,5 @@ "Visitor": "Visitor" } } - }, - "wallet": { - "bill": { - "Ai model": "Ai Model", - "App name": "App name", - "Audio Speech": "Audio Speech", - "Bill Module": "Bill Detail", - "Chars length": "Chars length", - "Data Length": "Data length", - "Dataset store": "", - "Duration": "Duration(s)", - "Extension Input Token Length": "Extension input tokens", - "Extension Output Token Length": "Extension output tokens", - "Extension result": "Extension result", - "Input Token Length": "Input tokens", - "Module name": "Module name", - "Next Step Guide": "", - "Number": "Bill ID", - "Output Token Length": "Output tokens", - "ReRank": "ReRank", - "Source": "Source", - "Text Length": "Text length", - "Time": "Time", - "Token Length": "Tokens", - "Total": "Total", - "Whisper": "Whisper", - "bill username": "User" - }, - "moduleName": { - "index": "Index Generation", - "qa": "QA Generation" - } } } diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index e12c9d08c..1accdb4db 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -15,6 +15,7 @@ "AI Settings": "AI 配置", "Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", "App Detail": "应用详情", + "Apps Share": "应用分享", "Basic Settings": "基本信息", "Chat Debug": "调试预览", "Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录", @@ -83,6 +84,7 @@ "Delete Success": "删除成功", "Delete Tip": "删除提示", "Delete Warning": "删除警告", + "Detail": "详情", "Done": "完成", "Edit": "编辑", "Exit": "退出", @@ -98,8 +100,6 @@ "Last use time": "最后使用时间", "Load Failed": "加载失败", "Loading": "加载中...", - "Max credit": "最大金额", - "Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。", "More settings": "更多设置", "Name": "名称", "Name Can": "名称不能为空", @@ -110,6 +110,7 @@ "Number of words": "{{amount}}字", "OK": "好的", "Opened": "已开启", + "Other": "其他", "Output": "输出", "Params": "参数", "Password inconsistency": "两次密码不一致", @@ -132,17 +133,20 @@ "Select template": "选择模板", "Set Avatar": "点击设置头像", "Set Name": "取个名字", + "Set Team Tags": "团队标签", "Setting": "设置", "Status": "状态", "Submit failed": "提交失败", "Submit success": "提交成功", "Team": "团队", + "Team Tags Set": "标签", "Test": "测试", "Time": "时间", "Un used": "未使用", "UnKnow": "未知", "UnKnow Source": "未知来源", "Unlimited": "无限制", + "Update": "更新", "Update Failed": "更新异常", "Update Success": "更新成功", "Update Successful": "更新成功", @@ -402,7 +406,7 @@ "Stop Speak": "停止录音", "Type a message": "输入问题", "Unpin": "取消置顶", - "You need to a chat app": "你需要创建一个应用", + "You need to a chat app": "鉴权失败,暂无权限访问应用", "error": { "Chat error": "对话出现异常", "Messages empty": "接口内容为空,可能文本超长了~", @@ -434,7 +438,7 @@ }, "response": { "Complete Response": "完整响应", - "Extension model": "问题补全模型", + "Extension model": "问题优化模型", "Plugin Resonse Detail": "插件详情", "Read complete response": "查看详情", "Read complete response tips": "点击查看详细流程", @@ -500,6 +504,7 @@ "Manual collection": "手动数据集", "My Dataset": "我的知识库", "Name": "知识库名称", + "Query extension intro": "开启问题优化功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。", "Quote Length": "引用内容长度", "Read Dataset": "查看知识库详情", "Search score tip": "{{scoreText}}下面是详细排名和得分情况:\n----\n{{detailScore}}", @@ -551,7 +556,8 @@ "success": "开始同步" } }, - "training": {} + "training": { + } }, "data": { "Auxiliary Data": "辅助数据", @@ -560,7 +566,7 @@ "Data Content": "相关数据内容", "Data Content Placeholder": "该输入框是必填项,该内容通常是对于知识点的描述,也可以是用户的问题,最多 {{maxToken}} 字。", "Data Content Tip": "该输入框是必填项\n该内容通常是对于知识点的描述,也可以是用户的问题。", - "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引,如不需要默认索引,可删除。 每条数据必须保证有一个以上索引,所有索引被删除后,会自动生成默认索引。", + "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引。", "Edit": "编辑数据", "Empty Tip": "这个集合还没有数据~", "Main Content": "主要内容", @@ -608,9 +614,10 @@ "Data file progress": "数据上传进度", "Data process params": "数据处理参数", "Down load csv template": "点击下载 CSV 模板", - "Embedding Estimated Price Tips": "索引计费: {{price}}/1k字符", + "Embedding Estimated Price Tips": "索引计费: {{price}}积分/1k字符", "Estimated Price": "预估价格: {{amount}}{{unit}}", - "Estimated Price Tips": "QA计费为\n输入: {{inputPrice}}/1k tokens\n输出: {{outputPrice}}/1k tokens", + "Estimated Price Tips": "QA计费为\n输入: 1k字符 = {{charsPointsPrice}}积分", + "Estimated points": "预估消耗 {{points}} 积分", "Fetch Error": "获取链接失败", "Fetch Url": "网络链接", "Fetch url placeholder": "最多10个链接,每行一个。", @@ -630,7 +637,7 @@ "Preview chunks": "分段预览", "Preview raw text": "预览源文本(最多展示10000字)", "Process way": "处理方式", - "QA Estimated Price Tips": "QA计费为: {{price}}元/1k 字符(包含输入和输出)", + "QA Estimated Price Tips": "QA计费为: {{price}}积分/1k 字符(包含输入和输出)", "QA Import": "QA拆分", "QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。", "Re Preview": "重新生成预览", @@ -680,7 +687,7 @@ "Source name": "引用来源名", "Top K": "单次搜索上限", "Using cfr": "", - "Using query extension": "使用问题补全", + "Using query extension": "使用问题优化", "mode": { "embedding": "语义检索", "embedding desc": "使用向量进行文本相关性查询", @@ -844,8 +851,8 @@ "dynamicTargetInput": "动态外部数据", "input": "输入框", "selectApp": "应用选择", - "selectChatModel": "对话模型选择", "selectDataset": "知识库选择", + "selectLLMModel": "对话模型选择", "switch": "开关", "target": "外部数据", "textarea": "段落输入" @@ -857,6 +864,7 @@ "Ai response content": "将在 stream 回复完毕后触发", "New context": "将本次回复内容拼接上历史记录,作为新的上下文返回", "Quote": "始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器", + "query extension result": "以字符串数组的形式输出,可将该结果直接连接到“知识库搜索”的“用户问题”中,建议不要连接到“AI对话”的“用户问题”中", "running done": "模块调用结束时触发" }, "label": { @@ -865,7 +873,7 @@ "Quote": "引用内容", "Search result empty": "搜索结果为空", "Search result not empty": "搜索结果不为空", - "cfr result": "补全结果", + "query extension result": "优化结果", "result false": "False", "result true": "True", "running done": "模块调用结束", @@ -892,8 +900,8 @@ "Http request": "HTTP 请求", "Http request intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)", "My plugin module": "个人插件", - "Query extension": "问题补全", - "Query extension intro": "开启问题补全功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。", + "Query extension": "问题优化", + "Query extension intro": "使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。", "Response module": "文本输出", "Running app": "应用调用", "Running app intro": "可以选择一个其他应用进行调用", @@ -1104,6 +1112,7 @@ "navbar": { "Account": "账号", "Apps": "应用", + "Apps Share": "应用分享", "Chat": "聊天", "Datasets": "知识库", "Module": "模块", @@ -1168,6 +1177,9 @@ "Usage": "已用额度(¥)" }, "outlink": { + "Max usage points": "积分上限", + "Max usage points tip": "该链接最多允许使用多少积分,超出后将无法使用。-1 代表无限制。", + "Usage points": "积分消耗", "share": { "Response Quote": "返回引用", "Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档" @@ -1176,9 +1188,16 @@ "subscription": { "Cancel subscription": "取消订阅" }, + "team": { + "limit": { + "No permission rerank": "无权使用结果重排,请升级您的套餐" + } + }, "user": { + "Avatar": "头像", "Need to login": "请先登录", "Price": "计费标准", + "User self info": "个人信息", "auth": { "Sending Code": "正在发送" }, @@ -1193,7 +1212,10 @@ } }, "wallet": { + "Amount": "金额", "Balance not enough tip": "余额不足,请先到账号页充值", + "Bills": "账单", + "Buy": "购买", "Buy more": "扩容", "Confirm pay": "支付确认", "Pay error": "支付失败", @@ -1201,57 +1223,115 @@ "bill": { "AI Model": "AI 模型", "AI Type": "AI 类型", - "Price": "价格(¥)" + "Number": "订单号", + "Price": "价格(¥)", + "Status": "状态", + "Type": "订单类型", + "payWay": { + "Way": "支付方式", + "balance": "余额支付", + "wx": "微信支付" + }, + "status": { + "closed": "已关闭", + "notpay": "未支付", + "refund": "已退款", + "success": "支付成功" + } }, + "moduleName": { + "index": "索引生成", + "qa": "QA 拆分" + }, + "noBill": "无账单记录~", "subscription": { + "AI points": "AI积分", "Ai points": "AI 积分计算标准", - "Buy now": "开始使用", + "Buy now": "切换套餐", "Change will take effect after the current subscription expires": "更新成功。将会再下个订阅周期生效。", "Current dataset store": "当前额外容量", - "Current plan": "当前计划", + "Current extra ai points": "当前额外积分", + "Current plan": "当前套餐", "Dataset store": "知识库容量", "Dataset store price tip": "每月1号从账号余额里扣除", "Expand size": "扩大容量", + "Extra ai points": "额外AI积分", "Extra dataset size": "额外知识库容量", - "Extra plan": "额外套餐", - "Extra plan tip": "标准套餐不够时,您可以购买额外套餐继续使用", + "Extra plan": "额外资源包", + "Extra plan tip": "标准套餐不够时,您可以购买额外资源包继续使用", "FAQ": "常见问题", - "Next plan": "未来计划", + "Month amount": "月数", + "Next extra ai points": "下次额外积分", + "Next plan": "未来套餐", "Next sub dataset size": "下次订阅额外容量", "Nonsupport": "无法切换", "Refund plan and pay confirm": "切换该套餐您无需支付额外费用,并将退换 {{amount}} 元至余额中。", + "Stand plan level": "订阅套餐", "Standard plan pay confirm": "切换该套餐,您本次需要补充支付 {{payPrice}} 元。", "Standard update fail": "修改订阅套餐异常", "Standard update success": "变更订阅套餐成功!", "Sub plan": "订阅套餐", "Sub plan tip": "免费使用 FastGPT 或升级更高的套餐", + "Team plan and usage": "套餐与用量", "Training weight": "训练优先级: {{weight}}", - "Update extra dataset size": "调整知识库额外容量", + "Update extra ai points": "额外AI积分", + "Update extra dataset size": "额外存储量", + "Upgrade plan": "升级套餐", "function": { "History store": "{{amount}} 天对话记录保留", "Max app": "{{amount}} 个应用与插件", "Max dataset": "{{amount}} 个知识库", - "Max dataset size": "{{amount}} 组知识库数据", + "Max dataset size": "{{amount}} 组知识库索引", "Max members": "{{amount}} 个团队成员", - "Points": "{{amount}}万AI积分" + "Points": "{{amount}} AI积分" }, "mode": { "Month": "按月", + "Period": "订阅周期", "Year": "按年", "Year sale": "赠送两个月" }, "standardSubLevel": { "enterprise": "企业版", "experience": "体验版", - "experience desc": "可使用 {{title}} 的完整功能", + "experience desc": "", "free": "免费版", - "free desc": "每月均可免费使用 {{title}} 的基础功能", + "free desc": "每月均可免费使用基础功能,15天不活跃时,将会清除知识库", "team": "团队版" }, "type": { + "balance": "余额充值", "extraDatasetSize": "知识库扩容", + "extraPoints": "AI积分套餐", "standard": "套餐订阅" } + }, + "usage": { + "Ai model": "AI模型", + "App name": "应用名", + "Audio Speech": "语音播报", + "Bill Module": "扣费模块", + "Chars length": "文本长度", + "Data Length": "数据长度", + "Dataset store": "知识库存储", + "Duration": "时长(秒)", + "Extension Input Token Length": "问题优化输入Tokens", + "Extension Output Token Length": "问题优化输出Tokens", + "Extension result": "问题优化结果", + "Input Token Length": "输入 Tokens", + "Module name": "模块名", + "Number": "", + "Output Token Length": "输出 Tokens", + "ReRank": "结果重排", + "Source": "来源", + "Text Length": "文本长度", + "Time": "生成时间", + "Token Length": "Token长度", + "Total": "总金额", + "Total points": "AI积分消耗", + "Usage Detail": "使用详情", + "Whisper": "语音输入", + "bill username": "用户" } } }, @@ -1293,6 +1373,7 @@ "Set OpenAI Account Failed": "设置 OpenAI 账号异常", "Sign Out": "登出", "Source": "来源", + "Standard Detail": "套餐详情", "Team": "团队", "Time": "时间", "Timezone": "时区", @@ -1340,7 +1421,11 @@ "Select Team": "团队选择", "Set Name": "给团队取个名字", "Switch Team Failed": "切换团队异常", + "Tags Async": "保存", "Team Name": "团队名", + "Team Tags Async": "标签同步", + "Team Tags Async Success": "链接报错成功,标签信息更新", + "Team Tags Async Tip": "填写标签同步连接,获取最新", "Update Team": "更新团队信息", "invite": { "Accept Confirm": "确认加入该团队?", @@ -1365,37 +1450,5 @@ "Visitor": "访客" } } - }, - "wallet": { - "bill": { - "Ai model": "AI模型", - "App name": "应用名", - "Audio Speech": "语音播报", - "Bill Module": "扣费模块", - "Chars length": "文本长度", - "Data Length": "数据长度", - "Dataset store": "知识库存储", - "Duration": "时长(秒)", - "Extension Input Token Length": "问题补全输入Tokens", - "Extension Output Token Length": "问题补全输出Tokens", - "Extension result": "问题补全结果", - "Input Token Length": "输入 Tokens", - "Module name": "模块名", - "Next Step Guide": "下一步指引", - "Number": "订单号", - "Output Token Length": "输出 Tokens", - "ReRank": "结果重排", - "Source": "来源", - "Text Length": "文本长度", - "Time": "生成时间", - "Token Length": "Token长度", - "Total": "总金额", - "Whisper": "语音输入", - "bill username": "用户" - }, - "moduleName": { - "index": "索引生成", - "qa": "QA 拆分" - } } } diff --git a/projects/app/src/components/ChatBox/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 6a57f74ef..5fc6e4508 100644 --- a/projects/app/src/components/ChatBox/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/WholeResponseModal.tsx @@ -8,10 +8,10 @@ import Tabs from '../Tabs'; import MyModal from '../MyModal'; import MyTooltip from '../MyTooltip'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import Markdown from '../Markdown'; import { QuoteList } from './QuoteModal'; import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants'; +import { formatNumber } from '@fastgpt/global/common/math/tools'; function Row({ label, @@ -131,10 +131,10 @@ const ResponseBox = React.memo(function ResponseBox({ <> - {activeModule?.price !== undefined && ( + {activeModule?.totalPoints !== undefined && ( )} - - diff --git a/projects/app/src/components/Layout/auth.tsx b/projects/app/src/components/Layout/auth.tsx index 09c28cad0..4129aa956 100644 --- a/projects/app/src/components/Layout/auth.tsx +++ b/projects/app/src/components/Layout/auth.tsx @@ -11,6 +11,7 @@ const unAuthPage: { [key: string]: boolean } = { '/login/fastlogin': true, '/appStore': true, '/chat/share': true, + '/chat/team': true, '/tools/price': true, '/price': true }; diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index 5561c25f9..2762c5678 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -23,6 +23,7 @@ const pcUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, + '/chat/team': true, '/app/edit': true, '/chat': true, '/tools/price': true, @@ -34,6 +35,7 @@ const phoneUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, + '/chat/team': true, '/tools/price': true, '/price': true }; @@ -114,9 +116,10 @@ const Layout = ({ children }: { children: JSX.Element }) => { )} + + {!!userInfo && } - {!!userInfo && } ); }; diff --git a/projects/app/src/components/Markdown/index.tsx b/projects/app/src/components/Markdown/index.tsx index 58f156a8b..e8eb3b67f 100644 --- a/projects/app/src/components/Markdown/index.tsx +++ b/projects/app/src/components/Markdown/index.tsx @@ -57,7 +57,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: className={`markdown ${styles.markdown} ${isChatting ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''} `} - remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]} + remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]} rehypePlugins={[RehypeKatex]} components={components} linkTarget={'_blank'} diff --git a/projects/app/src/components/Select/SelectAiModel.tsx b/projects/app/src/components/Select/SelectAiModel.tsx index 854c7b51c..328128f22 100644 --- a/projects/app/src/components/Select/SelectAiModel.tsx +++ b/projects/app/src/components/Select/SelectAiModel.tsx @@ -2,15 +2,15 @@ import React, { useMemo } from 'react'; import MySelect, { type SelectProps } from './index'; import { useTranslation } from 'next-i18next'; -import dynamic from 'next/dynamic'; -import { useDisclosure } from '@chakra-ui/react'; import { useSystemStore } from '@/web/common/system/useSystemStore'; - -const PriceBox = dynamic(() => import('@/components/support/wallet/Price')); +import { useRouter } from 'next/router'; +import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; const SelectAiModel = ({ list, ...props }: SelectProps) => { const { t } = useTranslation(); const { feConfigs } = useSystemStore(); + const router = useRouter(); + const expandList = useMemo(() => { return feConfigs.show_pay ? list.concat({ @@ -20,12 +20,6 @@ const SelectAiModel = ({ list, ...props }: SelectProps) => { : list; }, [feConfigs.show_pay, list, t]); - const { - isOpen: isOpenPriceBox, - onOpen: onOpenPriceBox, - onClose: onClosePriceBox - } = useDisclosure(); - return ( <> { {...props} onchange={(e) => { if (e === 'price') { - onOpenPriceBox(); + router.push(AI_POINT_USAGE_CARD_ROUTE); return; } props.onchange?.(e); }} /> - {isOpenPriceBox && } ); }; diff --git a/projects/app/src/components/TagEdit/index.tsx b/projects/app/src/components/TagEdit/index.tsx new file mode 100644 index 000000000..7f9a21809 --- /dev/null +++ b/projects/app/src/components/TagEdit/index.tsx @@ -0,0 +1,103 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { + Menu, + MenuButton, + MenuList, + MenuItemOption, + MenuOptionGroup, + Flex, + TagLabel, + TagCloseButton, + HStack, + Tag, + Input +} from '@chakra-ui/react'; +import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type'; +const TagEdit = ({ + defaultValues, + teamsTags, + setSelectedTags +}: { + defaultValues: []; + teamsTags: Array; + setSelectedTags: (item: Array) => void; +}) => { + const [teamTagsOptions, setTeamTagsOptions] = useState(teamsTags); + const setSelectTeamsTags = (item: any) => { + setSelectedTags(item); + }; + useMemo(() => { + setTeamTagsOptions(teamsTags); + }, [teamsTags]); + return ( + <> + + + + {teamsTags.map((item: TeamTagsSchema, index: number) => { + const key: string = item?.key; + if (defaultValues.indexOf(key as never) > -1) { + return ( + + {item.label} + + + ); + } + })} + + + + { + // 对用户输入的搜索文本进行小写转换,以实现不区分大小写的搜索 + const searchLower: string = e?.nativeEvent?.data || ''; + // 使用filter方法来过滤列表,只返回包含搜索文本的项 + const resultList = teamsTags.filter((item) => { + const searchValue = item.label || ''; + // 对列表中的每一项也进行小写转换 + return searchValue.includes(searchLower); + }); + !searchLower ? setTeamTagsOptions(teamsTags) : setTeamTagsOptions(resultList); + }} + /> + { + setSelectTeamsTags(e); + }} + > + {teamTagsOptions.map((item, index) => { + return ( + + {item?.label} + + ); + })} + + + + + ); +}; + +export default TagEdit; diff --git a/projects/app/src/components/core/module/DatasetParamsModal.tsx b/projects/app/src/components/core/module/DatasetParamsModal.tsx index d00a26df4..8b7c61e56 100644 --- a/projects/app/src/components/core/module/DatasetParamsModal.tsx +++ b/projects/app/src/components/core/module/DatasetParamsModal.tsx @@ -27,6 +27,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import Tabs from '@/components/Tabs'; import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor'; import SelectAiModel from '@/components/Select/SelectAiModel'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { useToast } from '@fastgpt/web/hooks/useToast'; export type DatasetParamsProps = { searchMode: `${DatasetSearchModeEnum}`; @@ -61,6 +63,8 @@ const DatasetParamsModal = ({ }: DatasetParamsProps & { onClose: () => void; onSuccess: (e: DatasetParamsProps) => void }) => { const { t } = useTranslation(); const theme = useTheme(); + const { toast } = useToast(); + const { teamPlanStatus } = useUserStore(); const { reRankModelList, llmModelList } = useSystemStore(); const [refresh, setRefresh] = useState(false); const [currentTabType, setCurrentTabType] = useState(SearchSettingTabEnum.searchMode); @@ -71,7 +75,7 @@ const DatasetParamsModal = ({ limit, similarity, searchMode, - usingReRank, + usingReRank: !!usingReRank && !!teamPlanStatus?.standardConstants?.permissionReRank, datasetSearchUsingExtensionQuery, datasetSearchExtensionModel: datasetSearchExtensionModel ?? llmModelList[0]?.model, datasetSearchExtensionBg @@ -105,6 +109,10 @@ const DatasetParamsModal = ({ return true; }, [getValues, similarity]); + const showReRank = useMemo(() => { + return usingReRank !== undefined && reRankModelList.length > 0; + }, [reRankModelList.length, usingReRank]); + return ( - {usingReRank !== undefined && reRankModelList.length > 0 && ( + {showReRank && ( <> { + if (!teamPlanStatus?.standardConstants?.permissionReRank) { + return toast({ + status: 'warning', + title: t('support.team.limit.No permission rerank') + }); + } setValue('usingReRank', !getValues('usingReRank')); setRefresh((state) => !state); }} @@ -273,7 +287,7 @@ const DatasetParamsModal = ({ {currentTabType === SearchSettingTabEnum.queryExtension && ( - {t('core.module.template.Query extension intro')} + {t('core.dataset.Query extension intro')} {t('core.dataset.search.Using query extension')} diff --git a/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx b/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx index 17af1b58b..d2a3a09be 100644 --- a/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx +++ b/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx @@ -46,11 +46,7 @@ const RenderList: { Component: dynamic(() => import('./templates/AiSetting')) }, { - types: [ - FlowNodeInputTypeEnum.selectChatModel, - FlowNodeInputTypeEnum.selectCQModel, - FlowNodeInputTypeEnum.selectExtractModel - ], + types: [FlowNodeInputTypeEnum.selectLLMModel], Component: dynamic(() => import('./templates/SelectAiModel')) }, { diff --git a/projects/app/src/components/core/module/Flow/index.tsx b/projects/app/src/components/core/module/Flow/index.tsx index d78b20cc1..46f139818 100644 --- a/projects/app/src/components/core/module/Flow/index.tsx +++ b/projects/app/src/components/core/module/Flow/index.tsx @@ -33,7 +33,7 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = { [FlowNodeTypeEnum.pluginInput]: dynamic(() => import('./components/nodes/NodePluginInput')), [FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodePluginOutput')), [FlowNodeTypeEnum.pluginModule]: NodeSimple, - [FlowNodeTypeEnum.cfr]: NodeSimple + [FlowNodeTypeEnum.queryExtension]: NodeSimple }; const edgeTypes = { [EDGE_TYPE]: ButtonEdge diff --git a/projects/app/src/components/support/apikey/Table.tsx b/projects/app/src/components/support/apikey/Table.tsx index f3f5c7ef9..19fdcb818 100644 --- a/projects/app/src/components/support/apikey/Table.tsx +++ b/projects/app/src/components/support/apikey/Table.tsx @@ -46,7 +46,7 @@ type EditProps = EditApiKeyProps & { _id?: string }; const defaultEditData: EditProps = { name: '', limit: { - credit: -1 + maxUsagePoints: -1 } }; @@ -153,16 +153,16 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => { - {apiKeys.map(({ _id, name, usage, limit, apiKey, createTime, lastUsedTime }) => ( + {apiKeys.map(({ _id, name, usagePoints, limit, apiKey, createTime, lastUsedTime }) => ( {name} {apiKey} - {usage} + {Math.round(usagePoints)} {feConfigs?.isPlus && ( <> - {limit?.credit && limit?.credit > -1 - ? `${limit?.credit}` + {limit?.maxUsagePoints && limit?.maxUsagePoints > -1 + ? `${limit?.maxUsagePoints}` : t('common.Unlimited')} @@ -334,15 +334,15 @@ function EditKeyModal({ <> - {t('common.Max credit')}: - + {t('support.outlink.Max usage points')}: + import('./EditModal')); const InviteModal = dynamic(() => import('./InviteModal')); +const TeamTagsAsync = dynamic(() => import('../TeamTagsAsync')); const TeamManageModal = ({ onClose }: { onClose: () => void }) => { const theme = useTheme(); const { t } = useTranslation(); const { Loading } = useLoading(); const { toast } = useToast(); + const [teamsTags, setTeamTags] = useState(); const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm(); const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({ @@ -61,6 +69,11 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { const { userInfo, initUserInfo } = useUserStore(); const [editTeamData, setEditTeamData] = useState(); const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure(); + const { + isOpen: isOpenTeamTagsAsync, + onOpen: onOpenTeamTagsAsync, + onClose: onCloseTeamTagsAsync + } = useDisclosure(); const { data: myTeams = [], @@ -76,6 +89,8 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { mutationFn: async (teamId: string) => { const token = await putSwitchTeam(teamId); token && setToken(token); + // get team tags + await getTeamsTags(teamId); return initUserInfo(); }, errorToast: t('user.team.Switch Team Failed') @@ -86,6 +101,11 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { ['getMembers', userInfo?.team?.teamId], () => { if (!userInfo?.team?.teamId) return []; + // get team tags + getTeamsTags(userInfo.team.teamId).then((res: any) => { + setTeamTags(res); + }); + return getTeamMembers(userInfo.team.teamId); } ); @@ -108,7 +128,9 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { mutationFn: async (teamId?: string) => { if (!teamId) return; // change to personal team + // get members await onSwitchTeam(defaultTeam.teamId); + return delLeaveTeam(teamId); }, onSuccess() { @@ -184,6 +206,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { bg: 'myGray.100' } })} + onClick={() => onSwitchTeam(team.teamId)} > void }) => { : {})} > {team.teamName} + {/* {userInfo?.team?.teamId === team.teamId && ( + + {teamsTags.slice(0, 3).map((item: any, index) => { + return ( + + {item.label} + + ); + })} + + )} */} {userInfo?.team?.teamId === team.teamId ? ( @@ -229,7 +263,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { borderBottomColor={'myGray.100'} mb={3} > - + {userInfo.team.teamName} {userInfo.team.role === TeamMemberRoleEnum.owner && ( @@ -279,6 +313,27 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { {t('user.team.Invite Member')} )} + {userInfo.team.role === TeamMemberRoleEnum.owner && ( + + )} {userInfo.team.role !== TeamMemberRoleEnum.owner && ( + + + + + + + + + ); +}; + +export default React.memo(TeamTagsAsync); diff --git a/projects/app/src/components/support/wallet/Price.tsx b/projects/app/src/components/support/wallet/Price.tsx deleted file mode 100644 index f102ec2a2..000000000 --- a/projects/app/src/components/support/wallet/Price.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React from 'react'; -import { Box, CloseButton } from '@chakra-ui/react'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import ReactDOM from 'react-dom'; - -import Markdown from '@/components/Markdown'; - -const Price = ({ onClose }: { onClose: () => void }) => { - const { llmModelList, vectorModelList, audioSpeechModelList, whisperModel } = useSystemStore(); - - const list = [ - { - title: 'AI语言模型', - describe: '', - md: ` -| 模型 | 输入价格(¥) | 输出价格(¥) | -| --- | --- | --- | -${llmModelList - ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k tokens | ${item.outputPrice}/1k tokens |`) - .join('\n')}` - }, - { - title: '索引模型(文档训练 & 文档检索)', - describe: '', - md: ` -| 模型 | 价格(¥) | -| --- | --- | -${vectorModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')} - ` - }, - { - title: '语音播放', - describe: '', - md: ` -| 模型 | 价格(¥) | -| --- | --- | -${audioSpeechModelList - ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 | - |`) - .join('\n')}` - }, - ...(whisperModel - ? [ - { - title: '语音输入', - describe: '', - md: ` -| 模型 | 价格(¥) | -| --- | --- | -| ${whisperModel.name} | ${whisperModel.inputPrice}/分钟 | - |` - } - ] - : []) - ]; - - return ReactDOM.createPortal( - - - - - {list.map((item) => ( - - - {item.title} - - - - - - ))} - - - , - // @ts-ignore - document.querySelector('body') - ); -}; - -export default Price; diff --git a/projects/app/src/components/support/wallet/QRCodePayModal.tsx b/projects/app/src/components/support/wallet/QRCodePayModal.tsx new file mode 100644 index 000000000..521479154 --- /dev/null +++ b/projects/app/src/components/support/wallet/QRCodePayModal.tsx @@ -0,0 +1,84 @@ +import MyModal from '@/components/MyModal'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'next-i18next'; +import { Box, ModalBody, ModalFooter } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { checkBalancePayResult } from '@/web/support/wallet/bill/api'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useRouter } from 'next/router'; +import { getErrText } from '@fastgpt/global/common/error/utils'; + +export type QRPayProps = { + readPrice: number; + codeUrl: string; + billId: string; +}; + +const QRCodePayModal = ({ + readPrice, + codeUrl, + billId, + onSuccess +}: QRPayProps & { onSuccess?: () => any }) => { + const router = useRouter(); + const { t } = useTranslation(); + const { toast } = useToast(); + const dom = document.getElementById('payQRCode'); + + useEffect(() => { + if (dom && window.QRCode) { + new window.QRCode(dom, { + text: codeUrl, + width: 128, + height: 128, + colorDark: '#000000', + colorLight: '#ffffff', + correctLevel: window.QRCode.CorrectLevel.H + }); + } + }, [dom]); + + useQuery( + [billId], + () => { + if (!billId) return null; + return checkBalancePayResult(billId); + }, + { + enabled: !!billId, + refetchInterval: 3000, + onSuccess: async (res) => { + if (!res) return; + + try { + await onSuccess?.(); + toast({ + title: res, + status: 'success' + }); + } catch (error) { + toast({ + title: getErrText(error), + status: 'error' + }); + } + + setTimeout(() => { + router.reload(); + }, 1000); + } + } + ); + + return ( + + + 请微信扫码支付: {readPrice}元,请勿关闭页面 + + + + + ); +}; + +export default React.memo(QRCodePayModal); diff --git a/projects/app/src/components/support/wallet/StandardPlanContentList.tsx b/projects/app/src/components/support/wallet/StandardPlanContentList.tsx new file mode 100644 index 000000000..6a5eda487 --- /dev/null +++ b/projects/app/src/components/support/wallet/StandardPlanContentList.tsx @@ -0,0 +1,116 @@ +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants'; +import React, { useMemo } from 'react'; +import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants'; +import { Box, Flex, Grid } from '@chakra-ui/react'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { useTranslation } from 'next-i18next'; + +const StandardPlanContentList = ({ + level, + mode +}: { + level: `${StandardSubLevelEnum}`; + mode: `${SubModeEnum}`; +}) => { + const { t } = useTranslation(); + const { subPlans } = useSystemStore(); + + const planContent = useMemo(() => { + const plan = subPlans?.standard?.[level]; + if (!plan) return; + return { + price: plan.price * (mode === SubModeEnum.month ? 1 : 10), + level: level as `${StandardSubLevelEnum}`, + ...standardSubLevelMap[level as `${StandardSubLevelEnum}`], + maxTeamMember: plan.maxTeamMember, + maxAppAmount: plan.maxAppAmount, + maxDatasetAmount: plan.maxDatasetAmount, + chatHistoryStoreDuration: plan.chatHistoryStoreDuration, + maxDatasetSize: plan.maxDatasetSize, + permissionCustomApiKey: plan.permissionCustomApiKey, + permissionCustomCopyright: plan.permissionCustomCopyright, + trainingWeight: plan.trainingWeight, + permissionReRank: plan.permissionReRank, + totalPoints: plan.totalPoints * (mode === SubModeEnum.month ? 1 : 12), + permissionWebsiteSync: plan.permissionWebsiteSync + }; + }, [subPlans?.standard, level, mode]); + + return planContent ? ( + + + + + {t('support.wallet.subscription.function.Max members', { + amount: planContent.maxTeamMember + })} + + + + + + {t('support.wallet.subscription.function.Max app', { + amount: planContent.maxAppAmount + })} + + + + + + {t('support.wallet.subscription.function.Max dataset', { + amount: planContent.maxDatasetAmount + })} + + + + + + {t('support.wallet.subscription.function.History store', { + amount: planContent.chatHistoryStoreDuration + })} + + + + + + {t('support.wallet.subscription.function.Max dataset size', { + amount: planContent.maxDatasetSize + })} + + + + + + + {t('support.wallet.subscription.function.Points', { + amount: planContent.totalPoints + })} + + + + + + + {t('support.wallet.subscription.Training weight', { + weight: planContent.trainingWeight + })} + + + {!!planContent.permissionReRank && ( + + + 检索结果重排 + + )} + {!!planContent.permissionWebsiteSync && ( + + + Web站点同步 + + )} + + ) : null; +}; + +export default StandardPlanContentList; diff --git a/projects/app/src/components/support/wallet/SubDatasetModal.tsx b/projects/app/src/components/support/wallet/SubDatasetModal.tsx deleted file mode 100644 index dcc100b10..000000000 --- a/projects/app/src/components/support/wallet/SubDatasetModal.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import React, { useState } from 'react'; -import MyModal from '@/components/MyModal'; -import { useTranslation } from 'next-i18next'; -import { - Box, - Flex, - ModalBody, - NumberInput, - NumberInputField, - NumberInputStepper, - NumberIncrementStepper, - NumberDecrementStepper, - ModalFooter, - Button -} from '@chakra-ui/react'; -import { useQuery } from '@tanstack/react-query'; -import { - getTeamDatasetValidSub, - posCheckTeamDatasetSizeSub, - postUpdateTeamDatasetSizeSub, - putTeamDatasetSubStatus -} from '@/web/support/wallet/sub/api'; -import Markdown from '@/components/Markdown'; -import MyTooltip from '@/components/MyTooltip'; -import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import { useConfirm } from '@/web/common/hooks/useConfirm'; -import { useRequest } from '@/web/common/hooks/useRequest'; -import { useRouter } from 'next/router'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; -import MySelect from '@/components/Select'; -import { - SubStatusEnum, - SubTypeEnum, - subSelectMap -} from '@fastgpt/global/support/wallet/sub/constants'; -import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; -import { useUserStore } from '@/web/support/user/useUserStore'; - -const SubDatasetModal = ({ onClose }: { onClose: () => void }) => { - const { subPlans } = useSystemStore(); - const datasetStorePrice = subPlans?.extraDatasetSize?.price || 0; - - const { t } = useTranslation(); - const router = useRouter(); - const { ConfirmModal, openConfirm } = useConfirm({}); - const { userInfo } = useUserStore(); - const [datasetSize, setDatasetSize] = useState(0); - const [isRenew, setIsRenew] = useState('false'); - - const { data: teamSubPlan } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub, { - onSuccess(res) { - setIsRenew(res?.extraDatasetSize?.status === SubStatusEnum.active ? 'true' : 'false'); - setDatasetSize((res?.extraDatasetSize?.nextExtraDatasetSize || 0) / 1000); - } - }); - - const { mutate: onClickUpdateSub, isLoading: isPaying } = useRequest({ - mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }), - onSuccess() { - setTimeout(() => { - router.reload(); - }, 100); - }, - successToast: t('common.Update success'), - errorToast: t('common.error.Update error') - }); - - const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({ - mutationFn: () => - posCheckTeamDatasetSizeSub({ - size: datasetSize - }), - onSuccess(res: SubDatasetSizePreviewCheckResponse) { - if (!res.payForNewSub) { - onClickUpdateSub(''); - return; - } else { - openConfirm( - () => { - if (!res.balanceEnough) return; - onClickUpdateSub(''); - }, - undefined, - - - 当前额外容量: - {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0}条 - - - 新的额外容量: - {res.newSubSize}条 - - - 新套餐价格: - {formatStorePrice2Read(res.newPlanPrice)}元 - - - 本次需支付: - {formatStorePrice2Read(res.payPrice)}元 - - - 有效时长: - 30天 - - - 账号余额: - {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}元 - - {!res.balanceEnough && ( - - 账号余额不足,请先充值余额再购买额外容量。 - - )} - - )(); - } - }, - errorToast: t('common.error.Update error') - }); - const { mutate: onUpdateStatus } = useRequest({ - mutationFn: (e: 'true' | 'false') => { - setIsRenew(e); - return putTeamDatasetSubStatus({ - status: subSelectMap[e], - type: SubTypeEnum.extraDatasetSize - }); - }, - successToast: t('common.Update success'), - errorToast: t('common.error.Update error') - }); - - const isLoading = isPaying || isFetchingPreviewCheck; - - return ( - - - <> - - {t('support.user.Price')} - - - - - - - - {t('support.wallet.subscription.Current dataset store')}: - - {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0} - {t('core.dataset.data.unit')} - - - {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize !== undefined && ( - - {t('support.wallet.subscription.Next sub dataset size')}: - - {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize || 0} - {t('core.dataset.data.unit')} - - - )} - {!!teamSubPlan?.extraDatasetSize?.startTime && ( - - 订阅开始时间: - {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.startTime)} - - )} - {!!teamSubPlan?.extraDatasetSize?.expiredTime && ( - - 订阅到期时间: - {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.expiredTime)} - - )} - - 是否自动续费: - - - - {t('support.wallet.subscription.Update extra dataset size')} - - { - setDatasetSize(Number(e)); - }} - > - - - - - - - 000{t('core.dataset.data.unit')} - - - - - - {datasetSize * 1000 !== teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize && ( - - )} - - - - - ); -}; - -export default SubDatasetModal; diff --git a/projects/app/src/constants/app.ts b/projects/app/src/constants/app.ts index 53651df43..de53fbab3 100644 --- a/projects/app/src/constants/app.ts +++ b/projects/app/src/constants/app.ts @@ -15,7 +15,8 @@ export const defaultApp: AppDetailType = { tmbId: '', permission: 'private', isOwner: false, - canWrite: false + canWrite: false, + teamTags: [''] }; export const defaultOutLinkForm: OutLinkEditType = { @@ -23,7 +24,7 @@ export const defaultOutLinkForm: OutLinkEditType = { responseDetail: false, limit: { QPM: 100, - credit: -1 + maxUsagePoints: -1 } }; diff --git a/projects/app/src/global/core/chat/api.d.ts b/projects/app/src/global/core/chat/api.d.ts index db20bed4d..654005c81 100644 --- a/projects/app/src/global/core/chat/api.d.ts +++ b/projects/app/src/global/core/chat/api.d.ts @@ -14,6 +14,12 @@ export type InitChatProps = { chatId?: string; loadCustomFeedbacks?: boolean; }; +/* ---------- chat ----------- */ +export type chatByTeamProps = { + teamId?: string; + appId?: string; + outLinkUid?: string; +}; export type InitOutLinkChatProps = { chatId?: string; shareId?: string; @@ -39,6 +45,7 @@ export type InitChatResponse = { /* ---------- history ----------- */ export type getHistoriesProps = { appId?: string; + authToken?: string; // share chat shareId?: string; outLinkUid?: string; // authToken/uid diff --git a/projects/app/src/global/core/prompt/AIChat.ts b/projects/app/src/global/core/prompt/AIChat.ts index a89f3cd87..ea2a18f80 100644 --- a/projects/app/src/global/core/prompt/AIChat.ts +++ b/projects/app/src/global/core/prompt/AIChat.ts @@ -4,42 +4,34 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [ { title: '标准模板', desc: '标准提示词,用于结构不固定的知识库。', - value: ` -{{q}} -{{a}} -` + value: `{{q}} +{{a}}` }, { title: '问答模板', desc: '适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答', - value: ` -<问题> + value: ` {{q}} - -<答案> + + {{a}} - -` +` }, { title: '标准严格模板', desc: '在标准模板基础上,对模型的回答做更严格的要求。', - value: ` -{{q}} -{{a}} -` + value: `{{q}} +{{a}}` }, { title: '严格问答模板', desc: '在问答模板基础上,对模型的回答做更严格的要求。', - value: ` -<问题> + value: ` {{q}} - -<答案> + + {{a}} - -` +` } ]; @@ -47,14 +39,16 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ { title: '标准模板', desc: '', - value: `使用 标记中的内容作为你的知识: + value: `使用 标记中的内容作为你的知识: + {{quote}} + 回答要求: - 如果你不清楚答案,你需要澄清。 -- 避免提及你是从 获取的知识。 -- 保持答案与 中描述的一致。 +- 避免提及你是从 获取的知识。 +- 保持答案与 中描述的一致。 - 使用 Markdown 语法优化回答格式。 - 使用与问题相同的语言回答。 @@ -65,7 +59,9 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ desc: '', value: `使用 标记中的问答对进行回答。 + {{quote}} + 回答要求: - 选择其中一个或多个问答对进行回答。 @@ -78,18 +74,20 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ { title: '标准严格模板', desc: '', - value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识: + value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识: + {{quote}} + 思考流程: -1. 判断问题是否与 标记中的内容有关。 +1. 判断问题是否与 标记中的内容有关。 2. 如果有关,你按下面的要求回答。 3. 如果无关,你直接拒绝回答本次问题。 回答要求: -- 避免提及你是从 获取的知识。 -- 保持答案与 中描述的一致。 +- 避免提及你是从 获取的知识。 +- 保持答案与 中描述的一致。 - 使用 Markdown 语法优化回答格式。 - 使用与问题相同的语言回答。 @@ -100,7 +98,9 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ desc: '', value: `忘记你已有的知识,仅使用 标记中的问答对进行回答。 + {{quote}} +} 思考流程: 1. 判断问题是否与 标记中的内容有关。 diff --git a/projects/app/src/pages/_error.tsx b/projects/app/src/pages/_error.tsx index 1c119ad0a..f2acb97a9 100644 --- a/projects/app/src/pages/_error.tsx +++ b/projects/app/src/pages/_error.tsx @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { serviceSideProps } from '@/web/common/utils/i18n'; import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { Box } from '@chakra-ui/react'; function Error() { const router = useRouter(); @@ -24,11 +25,12 @@ function Error() { }, []); return ( -

- 部分系统不兼容,导致页面崩溃。如果可以,请联系作者,反馈下具体操作和页面。 大部分是 苹果 的 - safari 浏览器导致,可以尝试更换 chrome - 浏览器。或者是因为开了中文翻译导致,请检查并关闭中文翻译。 -

+ + {`出现未捕获的异常。 +1. 私有部署用户,90%由于配置文件不正确导致。 +2. 部分系统不兼容相关API。大部分是苹果的safari 浏览器导致,可以尝试更换 chrome。 +3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。`} + ); } diff --git a/projects/app/src/pages/account/components/BillDetail.tsx b/projects/app/src/pages/account/components/BillDetail.tsx deleted file mode 100644 index 6d089c2cf..000000000 --- a/projects/app/src/pages/account/components/BillDetail.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useMemo } from 'react'; -import { - ModalBody, - Flex, - Box, - Table, - Thead, - Tbody, - Tr, - Th, - Td, - TableContainer -} from '@chakra-ui/react'; -import { BillItemType } from '@fastgpt/global/support/wallet/bill/type.d'; -import dayjs from 'dayjs'; -import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; -import MyModal from '@/components/MyModal'; -import { useTranslation } from 'next-i18next'; - -const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void }) => { - const { t } = useTranslation(); - const filterBillList = useMemo( - () => bill.list.filter((item) => item && item.moduleName), - [bill.list] - ); - - const { - hasModel, - hasTokens, - hasInputTokens, - hasOutputTokens, - hasCharsLen, - hasDuration, - hasDataLen, - hasDatasetSize - } = useMemo(() => { - let hasModel = false; - let hasTokens = false; - let hasInputTokens = false; - let hasOutputTokens = false; - let hasCharsLen = false; - let hasDuration = false; - let hasDataLen = false; - let hasDatasetSize = false; - - bill.list.forEach((item) => { - if (item.model !== undefined) { - hasModel = true; - } - if (typeof item.tokenLen === 'number') { - hasTokens = true; - } - if (typeof item.inputTokens === 'number') { - hasInputTokens = true; - } - if (typeof item.outputTokens === 'number') { - hasOutputTokens = true; - } - if (typeof item.charsLength === 'number') { - hasCharsLen = true; - } - if (typeof item.duration === 'number') { - hasDuration = true; - } - if (typeof item.datasetSize === 'number') { - hasDatasetSize = true; - } - }); - - return { - hasModel, - hasTokens, - hasInputTokens, - hasOutputTokens, - hasCharsLen, - hasDuration, - hasDataLen, - hasDatasetSize - }; - }, [bill.list]); - - return ( - - - {/* - {t('wallet.bill.bill username')}: - {t(bill.memberName)} - */} - - {t('wallet.bill.Number')}: - {bill.id} - - - {t('wallet.bill.Time')}: - {dayjs(bill.time).format('YYYY/MM/DD HH:mm:ss')} - - - {t('wallet.bill.App name')}: - {t(bill.appName) || '-'} - - - {t('wallet.bill.Source')}: - {t(BillSourceMap[bill.source]?.label)} - - - {t('wallet.bill.Total')}: - {bill.total}元 - - - - {t('wallet.bill.Bill Module')} - - - - - - - {hasModel && } - {hasTokens && } - {hasInputTokens && } - {hasOutputTokens && } - {hasCharsLen && } - {hasDuration && } - {hasDatasetSize && ( - - )} - - - - - {filterBillList.map((item, i) => ( - - - {hasModel && } - {hasTokens && } - {hasInputTokens && } - {hasOutputTokens && } - {hasCharsLen && } - {hasDuration && } - {hasDatasetSize && } - - - ))} - -
{t('wallet.bill.Module name')}{t('wallet.bill.Ai model')}{t('wallet.bill.Token Length')}{t('wallet.bill.Input Token Length')}{t('wallet.bill.Output Token Length')}{t('wallet.bill.Text Length')}{t('wallet.bill.Duration')}{t('support.wallet.subscription.type.extraDatasetSize')}费用(¥)
{t(item.moduleName)}{item.model ?? '-'}{item.tokenLen ?? '-'}{item.inputTokens ?? '-'}{item.outputTokens ?? '-'}{item.charsLength ?? '-'}{item.duration ?? '-'}{item.datasetSize ?? '-'}{formatStorePrice2Read(item.amount)}
-
-
-
-
- ); -}; - -export default BillDetail; diff --git a/projects/app/src/pages/account/components/BillTable.tsx b/projects/app/src/pages/account/components/BillTable.tsx index 8ea7eddd5..eb382a952 100644 --- a/projects/app/src/pages/account/components/BillTable.tsx +++ b/projects/app/src/pages/account/components/BillTable.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { + Button, Table, Thead, Tbody, @@ -9,43 +10,38 @@ import { TableContainer, Flex, Box, - Button + ModalBody } from '@chakra-ui/react'; -import { BillSourceEnum, BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; -import { getUserBills } from '@/web/support/wallet/bill/api'; -import type { BillItemType } from '@fastgpt/global/support/wallet/bill/type'; -import { usePagination } from '@/web/common/hooks/usePagination'; -import { useLoading } from '@/web/common/hooks/useLoading'; +import { getBills, checkBalancePayResult } from '@/web/support/wallet/bill/api'; +import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d'; import dayjs from 'dayjs'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; +import { useToast } from '@fastgpt/web/hooks/useToast'; import MyIcon from '@fastgpt/web/components/common/Icon'; -import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker'; -import { addDays } from 'date-fns'; -import dynamic from 'next/dynamic'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; import MySelect from '@/components/Select'; -import { useQuery } from '@tanstack/react-query'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { getTeamMembers } from '@/web/support/user/team/api'; -import Avatar from '@/components/Avatar'; -const BillDetail = dynamic(() => import('./BillDetail')); +import { + BillTypeEnum, + billPayWayMap, + billStatusMap, + billTypeMap +} from '@fastgpt/global/support/wallet/bill/constants'; +import { usePagination } from '@/web/common/hooks/usePagination'; +import MyBox from '@/components/common/MyBox'; +import { useRequest } from '@/web/common/hooks/useRequest'; +import MyModal from '@/components/MyModal'; +import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants'; const BillTable = () => { const { t } = useTranslation(); - const { Loading } = useLoading(); - const [dateRange, setDateRange] = useState({ - from: addDays(new Date(), -7), - to: new Date() - }); - const [billSource, setBillSource] = useState<`${BillSourceEnum}` | ''>(''); - const { isPc } = useSystemStore(); - const { userInfo } = useUserStore(); - const [billDetail, setBillDetail] = useState(); + const { toast } = useToast(); + const [billType, setBillType] = useState<`${BillTypeEnum}` | ''>(''); + const [billDetail, setBillDetail] = useState(); - const sourceList = useMemo( + const billTypeList = useMemo( () => [ { label: t('common.All'), value: '' }, - ...Object.entries(BillSourceMap).map(([key, value]) => ({ + ...Object.entries(billTypeMap).map(([key, value]) => ({ label: t(value.label), value: key })) @@ -53,134 +49,193 @@ const BillTable = () => { [t] ); - const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); - const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { - if (!userInfo?.team?.teamId) return []; - return getTeamMembers(userInfo.team.teamId); - }); - const tmbList = useMemo( - () => - members.map((item) => ({ - label: ( - - - {item.memberName} - - ), - value: item.tmbId - })), - [members] - ); - const { data: bills, isLoading, Pagination, - getData - } = usePagination({ - api: getUserBills, - pageSize: isPc ? 20 : 10, + getData, + total + } = usePagination({ + api: getBills, + pageSize: 20, params: { - dateStart: dateRange.from || new Date(), - dateEnd: addDays(dateRange.to || new Date(), 1), - source: billSource, - teamMemberId: selectTmbId + type: billType }, defaultRequest: false }); + const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({ + mutationFn: async (payId: string) => { + try { + const data = await checkBalancePayResult(payId); + toast({ + title: data, + status: 'success' + }); + } catch (error: any) { + toast({ + title: error?.message, + status: 'warning' + }); + console.log(error); + } + try { + getData(1); + } catch (error) {} + } + }); + useEffect(() => { getData(1); - }, [billSource, selectTmbId]); + }, [billType]); return ( - - - {tmbList.length > 1 && userInfo?.team?.canWrite && ( - - - {t('support.user.team.member')} - - - - )} - - - getData(1)} - /> - - - - + + - {/* */} - + - - + + + - {bills.map((item) => ( - - {/* */} - - - - + {bills.map((item, i) => ( + + + + + + ))}
{t('user.team.Member Name')}{t('user.Time')}# { - setBillSource(e); + setBillType(e); }} w={'130px'} > {t('user.Application Name')}{t('user.Total Amount')}{t('user.Time')}{t('support.wallet.Amount')}{t('support.wallet.bill.Status')}
{item.memberName}{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}{t(BillSourceMap[item.source]?.label)}{t(item.appName) || '-'}{item.total}元
{i + 1}{t(billTypeMap[item.type]?.label)} - {formatStorePrice2Read(item.price)}元{t(billStatusMap[item.status]?.label)} + {item.status === 'NOTPAY' && ( + + )} +
+ {total >= 20 && ( + + + + )} + {!isLoading && bills.length === 0 && ( + + + + {t('support.wallet.noBill')} + + + )}
- {!isLoading && bills.length === 0 && ( - - - - 无使用记录~ - - + {!!billDetail && ( + setBillDetail(undefined)} /> )} - - - {!!billDetail && setBillDetail(undefined)} />} -
+ ); }; -export default React.memo(BillTable); +export default BillTable; + +function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) { + const { t } = useTranslation(); + + return ( + + + + {t('support.wallet.bill.Number')}: + {bill.orderId} + + + {t('support.wallet.usage.Time')}: + {dayjs(bill.createTime).format('YYYY/MM/DD HH:mm:ss')} + + + {t('support.wallet.bill.Status')}: + {t(billStatusMap[bill.status]?.label)} + + {!!bill.metadata?.payWay && ( + + {t('support.wallet.bill.payWay.Way')}: + {t(billPayWayMap[bill.metadata.payWay]?.label)} + + )} + + {t('support.wallet.Amount')}: + {formatStorePrice2Read(bill.price)}元 + + + {t('support.wallet.bill.Type')}: + {t(billTypeMap[bill.type]?.label)} + + {!!bill.metadata?.subMode && ( + + {t('support.wallet.subscription.mode.Period')}: + {t(subModeMap[bill.metadata.subMode]?.label)} + + )} + {!!bill.metadata?.standSubLevel && ( + + {t('support.wallet.subscription.Stand plan level')}: + {t(standardSubLevelMap[bill.metadata.standSubLevel]?.label)} + + )} + {bill.metadata?.datasetSize !== undefined && ( + + {t('support.wallet.subscription.Extra dataset size')}: + {bill.metadata?.datasetSize} + + )} + {bill.metadata?.extraPoints !== undefined && ( + + {t('support.wallet.subscription.Extra ai points')}: + {bill.metadata.extraPoints} + + )} + + + ); +} diff --git a/projects/app/src/pages/account/components/Info.tsx b/projects/app/src/pages/account/components/Info.tsx index a957475be..6f6d45a75 100644 --- a/projects/app/src/pages/account/components/Info.tsx +++ b/projects/app/src/pages/account/components/Info.tsx @@ -1,15 +1,14 @@ -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Box, Flex, Button, useDisclosure, useTheme, - Divider, - Select, Input, Link, - Progress + Progress, + Grid } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { UserUpdateParams } from '@/types/user'; @@ -22,35 +21,68 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; -import { timezoneList } from '@fastgpt/global/common/time/timezone'; import Avatar from '@/components/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyTooltip from '@/components/MyTooltip'; -import { langMap, setLngStore } from '@/web/common/utils/i18n'; import { useRouter } from 'next/router'; -import MySelect from '@/components/Select'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; import { putUpdateMemberName } from '@/web/support/user/team/api'; import { getDocPath } from '@/web/common/system/doc'; -import { getTeamDatasetValidSub } from '@/web/support/wallet/sub/api'; import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; +import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants'; +import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; +import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; +import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList'; +const StandDetailModal = dynamic(() => import('./standardDetailModal')); const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu')); const PayModal = dynamic(() => import('./PayModal')); const UpdatePswModal = dynamic(() => import('./UpdatePswModal')); const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal')); -const SubDatasetModal = dynamic(() => import('@/components/support/wallet/SubDatasetModal')); -const UserInfo = () => { +const Account = () => { + const { isPc } = useSystemStore(); + + const { initUserInfo } = useUserStore(); + + useQuery(['init'], initUserInfo); + + return ( + + {isPc ? ( + + + + + + + + + + + + ) : ( + <> + + + + + )} + + ); +}; + +export default React.memo(Account); + +const MyInfo = () => { const theme = useTheme(); - const router = useRouter(); - const { feConfigs, systemVersion } = useSystemStore(); - const { t, i18n } = useTranslation(); - const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); - const timezones = useRef(timezoneList()); + const { feConfigs } = useSystemStore(); + const { t } = useTranslation(); + const { userInfo, updateUserInfo } = useUserStore(); const { reset } = useForm({ defaultValues: userInfo as UserType }); + const { isPc } = useSystemStore(); const { toast } = useToast(); const { @@ -63,13 +95,11 @@ const UserInfo = () => { onClose: onCloseUpdatePsw, onOpen: onOpenUpdatePsw } = useDisclosure(); - const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); const { - isOpen: isOpenSubDatasetModal, - onClose: onCloseSubDatasetModal, - onOpen: onOpenSubDatasetModal + isOpen: isOpenStandardModal, + onClose: onCloseStandardModal, + onOpen: onOpenStandardModal } = useDisclosure(); - const { File, onOpen: onOpenSelectFile } = useSelectFile({ fileType: '.jpg,.png', multiple: false @@ -117,81 +147,73 @@ const UserInfo = () => { [onclickSave, t, toast, userInfo] ); - useQuery(['init'], initUserInfo, { - onSuccess(res) { - reset(res); - } - }); - - const { - data: teamSubPlan = { totalPoints: 0, usedPoints: 0, datasetMaxSize: 800, usedDatasetSize: 0 } - } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub); - const datasetUsageMap = useMemo(() => { - const rate = teamSubPlan.usedDatasetSize / teamSubPlan.datasetMaxSize; - - const colorScheme = (() => { - if (rate < 0.5) return 'green'; - if (rate < 0.8) return 'yellow'; - return 'red'; - })(); - - return { - colorScheme, - value: rate * 100, - maxSize: teamSubPlan.datasetMaxSize || t('common.Unlimited'), - usedSize: teamSubPlan.usedDatasetSize - }; - }, [teamSubPlan.usedDatasetSize, teamSubPlan.datasetMaxSize, t]); - return ( - - - - - - - - - - - {t('user.Replace')} + + {/* user info */} + {isPc && ( + + + {t('support.user.User self info')} - - + )} + + + {isPc ? ( + + {t('support.user.Avatar')}:  + + + + + + + + ) : ( + + + + + + + + + + {t('user.Replace')} + + + )} {feConfigs.isPlus && ( - + {t('user.Member Name')}:  { @@ -204,109 +226,265 @@ const UserInfo = () => { /> )} - + {t('user.Account')}:  {userInfo?.username} - + {feConfigs.isPlus && ( + + {t('user.Password')}:  + ***** + + + )} + {t('user.Team')}:  - - {t('user.Language')}:  - - ({ - label: lang.label, - value: key - }))} - onchange={(val: any) => { - const lang = val; - setLngStore(lang); - router.replace(router.basePath, router.asPath, { locale: lang }); - }} - /> + {feConfigs.isPlus && ( + + + + {t('user.team.Balance')}:  + + + {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 + + {feConfigs?.show_pay && userInfo?.team?.canWrite && ( + + )} + + )} + + {isOpenPayModal && } + {isOpenUpdatePsw && } + + + ); +}; +const PlanUsage = () => { + const { isPc } = useSystemStore(); + const router = useRouter(); + const { t } = useTranslation(); + const { userInfo, initUserInfo, teamPlanStatus } = useUserStore(); + const { reset } = useForm({ + defaultValues: userInfo as UserType + }); + + const planName = useMemo(() => { + if (!teamPlanStatus?.standard?.currentSubLevel) return ''; + return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label; + }, [teamPlanStatus?.standard?.currentSubLevel]); + const standardPlan = teamPlanStatus?.standard; + + useQuery(['init'], initUserInfo, { + onSuccess(res) { + reset(res); + } + }); + + const datasetUsageMap = useMemo(() => { + if (!teamPlanStatus) { + return { + colorScheme: 'green', + value: 0, + maxSize: t('common.Unlimited'), + usedSize: 0 + }; + } + const rate = teamPlanStatus.usedDatasetSize / teamPlanStatus.datasetMaxSize; + + const colorScheme = (() => { + if (rate < 0.5) return 'green'; + if (rate < 0.8) return 'yellow'; + return 'red'; + })(); + + return { + colorScheme, + value: rate * 100, + maxSize: teamPlanStatus.datasetMaxSize || t('common.Unlimited'), + usedSize: teamPlanStatus.usedDatasetSize + }; + }, [teamPlanStatus, t]); + const aiPointsUsageMap = useMemo(() => { + if (!teamPlanStatus) { + return { + colorScheme: 'green', + value: 0, + maxSize: t('common.Unlimited'), + usedSize: 0 + }; + } + + const rate = teamPlanStatus.usedPoints / teamPlanStatus.totalPoints; + + const colorScheme = (() => { + if (rate < 0.5) return 'green'; + if (rate < 0.8) return 'yellow'; + return 'red'; + })(); + + return { + colorScheme, + value: rate * 100, + maxSize: teamPlanStatus.totalPoints || t('common.Unlimited'), + usedSize: teamPlanStatus.usedPoints + }; + }, [teamPlanStatus, t]); + + return standardPlan ? ( + + + + + {t('support.wallet.subscription.Team plan and usage')} - - {t('user.Timezone')}:  - - - - {t('user.Password')}:  - ***** - + {/* */} + + + + + + {t('support.wallet.subscription.Current plan')} + + + {t(planName)} + + + {t('common.Expired Time')}: + {formatTime2YMDHM(standardPlan?.expiredTime)} + + + - {feConfigs.isPlus && ( - <> - - - - {t('user.team.Balance')}:  - - - {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 - - {feConfigs?.show_pay && userInfo?.team?.canWrite && ( - - )} - - - {feConfigs?.show_pay && ( - - - - {t('support.user.team.Dataset usage')}: {datasetUsageMap.usedSize}/ - {datasetUsageMap.maxSize} - - {userInfo?.team?.canWrite && ( - - )} - - - - + + + + + + + + + + + {t('support.user.team.Dataset usage')} + + {datasetUsageMap.usedSize}/{datasetUsageMap.maxSize} - )} - - )} + + + + + + + + + + {t('support.wallet.subscription.AI points')} + + {Math.round(teamPlanStatus.usedPoints)}/{teamPlanStatus.totalPoints} + + + + + + + + + + + ) : null; +}; +const Other = () => { + const theme = useTheme(); + const { toast } = useToast(); + const { feConfigs, systemVersion } = useSystemStore(); + const { t } = useTranslation(); + const { userInfo, updateUserInfo, initUserInfo, teamPlanStatus } = useUserStore(); + const { reset } = useForm({ + defaultValues: userInfo as UserType + }); + const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); + + const onclickSave = useCallback( + async (data: UserType) => { + await updateUserInfo({ + avatar: data.avatar, + timezone: data.timezone, + openaiAccount: data.openaiAccount + }); + reset(data); + toast({ + title: '更新数据成功', + status: 'success' + }); + }, + [reset, toast, updateUserInfo] + ); + + return ( + + {feConfigs?.docUrl && ( { )} - {feConfigs?.chatbotUrl && ( - + + + {t('common.system.Help Chatbot')} + + + + {feConfigs?.show_openai_account && ( + - + - {t('common.system.Help Chatbot')} + OpenAI/OneAPI 账号 - + + )} - {feConfigs?.show_openai_account && ( - <> - + - - - - - OpenAI/OneAPI 账号 - - - - - - )} - - - {isOpenPayModal && } - {isOpenUpdatePsw && } {isOpenOpenai && userInfo && ( { onClose={onCloseOpenai} /> )} - {isOpenSubDatasetModal && } - ); }; - -export default React.memo(UserInfo); diff --git a/projects/app/src/pages/account/components/InfoOld.tsx b/projects/app/src/pages/account/components/InfoOld.tsx new file mode 100644 index 000000000..83e16173f --- /dev/null +++ b/projects/app/src/pages/account/components/InfoOld.tsx @@ -0,0 +1,439 @@ +import React, { useCallback, useMemo, useRef } from 'react'; +import { + Box, + Flex, + Button, + useDisclosure, + useTheme, + Divider, + Select, + Input, + Link, + Progress +} from '@chakra-ui/react'; +import { useForm } from 'react-hook-form'; +import { UserUpdateParams } from '@/types/user'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import type { UserType } from '@fastgpt/global/support/user/type.d'; +import { useQuery } from '@tanstack/react-query'; +import dynamic from 'next/dynamic'; +import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; +import { compressImgFileAndUpload } from '@/web/common/file/controller'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useTranslation } from 'next-i18next'; +import { timezoneList } from '@fastgpt/global/common/time/timezone'; +import Avatar from '@/components/Avatar'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import MyTooltip from '@/components/MyTooltip'; +import { langMap, setLngStore } from '@/web/common/utils/i18n'; +import { useRouter } from 'next/router'; +import MySelect from '@/components/Select'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; +import { putUpdateMemberName } from '@/web/support/user/team/api'; +import { getDocPath } from '@/web/common/system/doc'; +import { getTeamPlanStatus } from '@/web/support/wallet/sub/api'; +import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; + +const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu')); +const PayModal = dynamic(() => import('./PayModal')); +const UpdatePswModal = dynamic(() => import('./UpdatePswModal')); +const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal')); + +const UserInfo = () => { + const theme = useTheme(); + const router = useRouter(); + const { feConfigs, systemVersion } = useSystemStore(); + const { t, i18n } = useTranslation(); + const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); + const timezones = useRef(timezoneList()); + const { reset } = useForm({ + defaultValues: userInfo as UserType + }); + + const { toast } = useToast(); + const { + isOpen: isOpenPayModal, + onClose: onClosePayModal, + onOpen: onOpenPayModal + } = useDisclosure(); + const { + isOpen: isOpenUpdatePsw, + onClose: onCloseUpdatePsw, + onOpen: onOpenUpdatePsw + } = useDisclosure(); + const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); + + const { File, onOpen: onOpenSelectFile } = useSelectFile({ + fileType: '.jpg,.png', + multiple: false + }); + + const onclickSave = useCallback( + async (data: UserType) => { + await updateUserInfo({ + avatar: data.avatar, + timezone: data.timezone, + openaiAccount: data.openaiAccount + }); + reset(data); + toast({ + title: '更新数据成功', + status: 'success' + }); + }, + [reset, toast, updateUserInfo] + ); + + const onSelectFile = useCallback( + async (e: File[]) => { + const file = e[0]; + if (!file || !userInfo) return; + try { + const src = await compressImgFileAndUpload({ + type: MongoImageTypeEnum.userAvatar, + file, + maxW: 300, + maxH: 300 + }); + + onclickSave({ + ...userInfo, + avatar: src + }); + } catch (err: any) { + toast({ + title: typeof err === 'string' ? err : t('common.error.Select avatar failed'), + status: 'warning' + }); + } + }, + [onclickSave, t, toast, userInfo] + ); + + useQuery(['init'], initUserInfo, { + onSuccess(res) { + reset(res); + } + }); + + const { + data: teamSubPlan = { + totalPoints: 0, + usedPoints: 0, + datasetMaxSize: 800, + usedDatasetSize: 0 + } + } = useQuery(['getTeamPlanStatus'], getTeamPlanStatus); + const datasetUsageMap = useMemo(() => { + const rate = teamSubPlan.usedDatasetSize / teamSubPlan.datasetMaxSize; + + const colorScheme = (() => { + if (rate < 0.5) return 'green'; + if (rate < 0.8) return 'yellow'; + return 'red'; + })(); + + return { + colorScheme, + value: rate * 100, + maxSize: teamSubPlan.datasetMaxSize || t('common.Unlimited'), + usedSize: teamSubPlan.usedDatasetSize + }; + }, [teamSubPlan.usedDatasetSize, teamSubPlan.datasetMaxSize, t]); + const aiPointsUsageMap = useMemo(() => { + const rate = teamSubPlan.usedPoints / teamSubPlan.totalPoints; + + const colorScheme = (() => { + if (rate < 0.5) return 'green'; + if (rate < 0.8) return 'yellow'; + return 'red'; + })(); + + return { + colorScheme, + value: rate * 100, + maxSize: teamSubPlan.totalPoints || t('common.Unlimited'), + usedSize: teamSubPlan.usedPoints + }; + }, [teamSubPlan.usedPoints, teamSubPlan.totalPoints, t]); + + return ( + + + + + + + + + + + {t('user.Replace')} + + + + {feConfigs.isPlus && ( + + {t('user.Member Name')}:  + { + const val = e.target.value; + if (val === userInfo?.team?.memberName) return; + try { + putUpdateMemberName(val); + } catch (error) {} + }} + /> + + )} + + {t('user.Account')}:  + {userInfo?.username} + + + {t('user.Team')}:  + + + + + + {t('user.Language')}:  + + ({ + label: lang.label, + value: key + }))} + onchange={(val: any) => { + const lang = val; + setLngStore(lang); + router.replace(router.basePath, router.asPath, { locale: lang }); + }} + /> + + + + {t('user.Timezone')}:  + + + + {t('user.Password')}:  + ***** + + + {feConfigs.isPlus && ( + <> + + + + {t('user.team.Balance')}:  + + + {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 + + {feConfigs?.show_pay && userInfo?.team?.canWrite && ( + + )} + + + {feConfigs?.show_pay && ( + <> + + + + {t('support.user.team.Dataset usage')}: {datasetUsageMap.usedSize}/ + {datasetUsageMap.maxSize} + + {userInfo?.team?.canWrite && ( + + )} + + + + + + + + + AI积分: {Math.round(teamSubPlan.usedPoints)}/{teamSubPlan.totalPoints} + + + + + + + + )} + + )} + + {feConfigs?.docUrl && ( + + + + {t('system.Help Document')} + + + + V{systemVersion} + + + )} + {feConfigs?.chatbotUrl && ( + + + + {t('common.system.Help Chatbot')} + + + )} + {feConfigs?.show_openai_account && ( + <> + + + + + + + OpenAI/OneAPI 账号 + + + + + + )} + + + {isOpenPayModal && } + {isOpenUpdatePsw && } + {isOpenOpenai && userInfo && ( + + onclickSave({ + ...userInfo, + openaiAccount: data + }) + } + onClose={onCloseOpenai} + /> + )} + + + ); +}; + +export default React.memo(UserInfo); diff --git a/projects/app/src/pages/account/components/PayModal.tsx b/projects/app/src/pages/account/components/PayModal.tsx index a397f409f..cbcb8d23d 100644 --- a/projects/app/src/pages/account/components/PayModal.tsx +++ b/projects/app/src/pages/account/components/PayModal.tsx @@ -1,37 +1,45 @@ import React, { useState, useCallback } from 'react'; import { ModalFooter, ModalBody, Button, Input, Box, Grid } from '@chakra-ui/react'; -import { getPayCode, checkPayResult } from '@/web/support/wallet/pay/api'; +import { getWxPayQRCode } from '@/web/support/wallet/bill/api'; import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { getErrText } from '@fastgpt/global/common/error/utils'; import { useTranslation } from 'next-i18next'; -import Markdown from '@/components/Markdown'; import MyModal from '@/components/MyModal'; +import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; -const PayModal = ({ onClose }: { onClose: () => void }) => { +import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal'; + +const PayModal = ({ + onClose, + defaultValue, + onSuccess +}: { + defaultValue?: number; + onClose: () => void; + onSuccess?: () => any; +}) => { const router = useRouter(); const { t } = useTranslation(); const { toast } = useToast(); - const [inputVal, setInputVal] = useState(''); + const [inputVal, setInputVal] = useState(defaultValue); const [loading, setLoading] = useState(false); - const [payId, setPayId] = useState(''); + const [qrPayData, setQRPayData] = useState(); const handleClickPay = useCallback(async () => { if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return; setLoading(true); try { // 获取支付二维码 - const res = await getPayCode(inputVal); - new window.QRCode(document.getElementById('payQRCode'), { - text: res.codeUrl, - width: 128, - height: 128, - colorDark: '#000000', - colorLight: '#ffffff', - correctLevel: window.QRCode.CorrectLevel.H + const res = await getWxPayQRCode({ + type: BillTypeEnum.balance, + balance: inputVal + }); + setQRPayData({ + readPrice: res.readPrice, + codeUrl: res.codeUrl, + billId: res.billId }); - setPayId(res.payId); } catch (err) { toast({ title: getErrText(err), @@ -41,84 +49,48 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { setLoading(false); }, [inputVal, toast]); - useQuery( - [payId], - () => { - if (!payId) return null; - return checkPayResult(payId); - }, - { - enabled: !!payId, - refetchInterval: 3000, - onSuccess(res) { - if (!res) return; - toast({ - title: res, - status: 'success' - }); - router.reload(); - } - } - ); - return ( - + - {!payId && ( - <> - - {[10, 20, 50, 100, 200, 500].map((item) => ( - - ))} - - - { - setInputVal(Math.floor(+e.target.value)); - }} - > - - - )} - {/* 付费二维码 */} - - {payId && 请微信扫码支付: {inputVal}元,请勿关闭页面} - + + {[10, 20, 50, 100, 200, 500].map((item) => ( + + ))} + + + { + setInputVal(Math.floor(+e.target.value)); + }} + > - {!payId && ( - <> - - - - )} + + + + {!!qrPayData && } ); }; diff --git a/projects/app/src/pages/account/components/PayRecordTable.tsx b/projects/app/src/pages/account/components/PayRecordTable.tsx deleted file mode 100644 index e5567764b..000000000 --- a/projects/app/src/pages/account/components/PayRecordTable.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useState, useCallback } from 'react'; -import { - Button, - Table, - Thead, - Tbody, - Tr, - Th, - Td, - TableContainer, - Flex, - Box -} from '@chakra-ui/react'; -import { getPayOrders, checkPayResult } from '@/web/support/wallet/pay/api'; -import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d'; -import dayjs from 'dayjs'; -import { useQuery } from '@tanstack/react-query'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useLoading } from '@/web/common/hooks/useLoading'; -import MyIcon from '@fastgpt/web/components/common/Icon'; - -const PayRecordTable = () => { - const { Loading, setIsLoading } = useLoading(); - const [payOrders, setPayOrders] = useState([]); - const { toast } = useToast(); - - const { isInitialLoading, refetch } = useQuery(['initPayOrder'], getPayOrders, { - onSuccess(res) { - setPayOrders(res); - } - }); - - const handleRefreshPayOrder = useCallback( - async (payId: string) => { - setIsLoading(true); - - try { - const data = await checkPayResult(payId); - toast({ - title: data, - status: 'success' - }); - } catch (error: any) { - toast({ - title: error?.message, - status: 'warning' - }); - console.log(error); - } - try { - refetch(); - } catch (error) {} - - setIsLoading(false); - }, - [refetch, setIsLoading, toast] - ); - - return ( - - {!isInitialLoading && payOrders.length === 0 ? ( - - - - 无支付记录~ - - - ) : ( - - - - - - - - - - - - - {payOrders.map((item) => ( - - - - - - - - ))} - -
订单号时间金额状态
{item.orderId} - {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} - {formatStorePrice2Read(item.price)}元{item.status} - {item.status === 'NOTPAY' && ( - - )} -
-
- )} - -
- ); -}; - -export default PayRecordTable; diff --git a/projects/app/src/pages/account/components/UsageDetail.tsx b/projects/app/src/pages/account/components/UsageDetail.tsx new file mode 100644 index 000000000..d75cd5c2c --- /dev/null +++ b/projects/app/src/pages/account/components/UsageDetail.tsx @@ -0,0 +1,119 @@ +import React, { useMemo } from 'react'; +import { + ModalBody, + Flex, + Box, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + TableContainer +} from '@chakra-ui/react'; +import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type.d'; +import dayjs from 'dayjs'; +import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; +import MyModal from '@/components/MyModal'; +import { useTranslation } from 'next-i18next'; +import { formatNumber } from '@fastgpt/global/common/math/tools'; + +const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () => void }) => { + const { t } = useTranslation(); + const filterBillList = useMemo( + () => usage.list.filter((item) => item && item.moduleName), + [usage.list] + ); + + const { hasModel, hasCharsLen, hasDuration } = useMemo(() => { + let hasModel = false; + let hasCharsLen = false; + let hasDuration = false; + let hasDataLen = false; + + usage.list.forEach((item) => { + if (item.model !== undefined) { + hasModel = true; + } + + if (typeof item.charsLength === 'number') { + hasCharsLen = true; + } + if (typeof item.duration === 'number') { + hasDuration = true; + } + }); + + return { + hasModel, + hasCharsLen, + hasDuration, + hasDataLen + }; + }, [usage.list]); + + return ( + + + + {t('support.wallet.bill.Number')}: + {usage.id} + + + {t('support.wallet.usage.Time')}: + {dayjs(usage.time).format('YYYY/MM/DD HH:mm:ss')} + + + {t('support.wallet.usage.App name')}: + {t(usage.appName) || '-'} + + + {t('support.wallet.usage.Source')}: + {t(UsageSourceMap[usage.source]?.label)} + + + {t('support.wallet.usage.Total points')}: + {formatNumber(usage.totalPoints)} + + + + {t('support.wallet.usage.Bill Module')} + + + + + + + {hasModel && } + {hasCharsLen && } + {hasDuration && } + + + + + + {filterBillList.map((item, i) => ( + + + {hasModel && } + {hasCharsLen && } + {hasDuration && } + + + ))} + +
{t('support.wallet.usage.Module name')}{t('support.wallet.usage.Ai model')}{t('support.wallet.usage.Text Length')}{t('support.wallet.usage.Duration')}{t('support.wallet.usage.Total points')}
{t(item.moduleName)}{item.model ?? '-'}{item.charsLength ?? '-'}{item.duration ?? '-'}{formatNumber(item.amount)}
+
+
+
+
+ ); +}; + +export default UsageDetail; diff --git a/projects/app/src/pages/account/components/UsageTable.tsx b/projects/app/src/pages/account/components/UsageTable.tsx new file mode 100644 index 000000000..d73f779de --- /dev/null +++ b/projects/app/src/pages/account/components/UsageTable.tsx @@ -0,0 +1,189 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { + Table, + Thead, + Tbody, + Tr, + Th, + Td, + TableContainer, + Flex, + Box, + Button +} from '@chakra-ui/react'; +import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; +import { getUserUsages } from '@/web/support/wallet/usage/api'; +import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type'; +import { usePagination } from '@/web/common/hooks/usePagination'; +import { useLoading } from '@/web/common/hooks/useLoading'; +import dayjs from 'dayjs'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker'; +import { addDays } from 'date-fns'; +import dynamic from 'next/dynamic'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useTranslation } from 'next-i18next'; +import MySelect from '@/components/Select'; +import { useQuery } from '@tanstack/react-query'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import Avatar from '@/components/Avatar'; +import { formatNumber } from '../../../../../../packages/global/common/math/tools'; +const UsageDetail = dynamic(() => import('./UsageDetail')); + +const UsageTable = () => { + const { t } = useTranslation(); + const { Loading } = useLoading(); + const [dateRange, setDateRange] = useState({ + from: addDays(new Date(), -7), + to: new Date() + }); + const [usageSource, setUsageSource] = useState<`${UsageSourceEnum}` | ''>(''); + const { isPc } = useSystemStore(); + const { userInfo } = useUserStore(); + const [usageDetail, setUsageDetail] = useState(); + + const sourceList = useMemo( + () => [ + { label: t('common.All'), value: '' }, + ...Object.entries(UsageSourceMap).map(([key, value]) => ({ + label: t(value.label), + value: key + })) + ], + [t] + ); + + const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); + const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { + if (!userInfo?.team?.teamId) return []; + return getTeamMembers(userInfo.team.teamId); + }); + const tmbList = useMemo( + () => + members.map((item) => ({ + label: ( + + + {item.memberName} + + ), + value: item.tmbId + })), + [members] + ); + + const { + data: usages, + isLoading, + Pagination, + getData + } = usePagination({ + api: getUserUsages, + pageSize: isPc ? 20 : 10, + params: { + dateStart: dateRange.from || new Date(), + dateEnd: addDays(dateRange.to || new Date(), 1), + source: usageSource, + teamMemberId: selectTmbId + }, + defaultRequest: false + }); + + useEffect(() => { + getData(1); + }, [usageSource, selectTmbId]); + + return ( + + + {tmbList.length > 1 && userInfo?.team?.canWrite && ( + + + {t('support.user.team.member')} + + + + )} + + + getData(1)} + /> + + + + + + + + {/* */} + + + + + + + + + {usages.map((item) => ( + + {/* */} + + + + + + + ))} + +
{t('user.team.Member Name')}{t('user.Time')} + { + setUsageSource(e); + }} + w={'130px'} + > + {t('user.Application Name')}{t('support.wallet.usage.Total points')}
{item.memberName}{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}{t(UsageSourceMap[item.source]?.label) || '-'}{t(item.appName) || '-'}{formatNumber(item.totalPoints) || 0} + +
+
+ + {!isLoading && usages.length === 0 && ( + + + + 无使用记录~ + + + )} + + + {!!usageDetail && ( + setUsageDetail(undefined)} /> + )} +
+ ); +}; + +export default React.memo(UsageTable); diff --git a/projects/app/src/pages/account/components/standardDetailModal.tsx b/projects/app/src/pages/account/components/standardDetailModal.tsx new file mode 100644 index 000000000..2c313980b --- /dev/null +++ b/projects/app/src/pages/account/components/standardDetailModal.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react'; +import MyModal from '@/components/MyModal'; +import { useTranslation } from 'next-i18next'; +import { useForm } from 'react-hook-form'; +import { useRequest } from '@/web/common/hooks/useRequest'; +import type { UserType } from '@fastgpt/global/support/user/type.d'; + +const StandDetailModal = ({ onClose }: { onClose: () => void }) => { + const { t } = useTranslation(); + + return ( + + + + + ); +}; + +export default StandDetailModal; diff --git a/projects/app/src/pages/account/index.tsx b/projects/app/src/pages/account/index.tsx index 774a41562..49ce40a7a 100644 --- a/projects/app/src/pages/account/index.tsx +++ b/projects/app/src/pages/account/index.tsx @@ -14,18 +14,16 @@ import { useTranslation } from 'next-i18next'; import Script from 'next/script'; const Promotion = dynamic(() => import('./components/Promotion')); +const UsageTable = dynamic(() => import('./components/UsageTable')); const BillTable = dynamic(() => import('./components/BillTable')); -const PayRecordTable = dynamic(() => import('./components/PayRecordTable')); const InformTable = dynamic(() => import('./components/InformTable')); const ApiKeyTable = dynamic(() => import('./components/ApiKeyTable')); -const PriceBox = dynamic(() => import('@/components/support/wallet/Price')); enum TabEnum { 'info' = 'info', 'promotion' = 'promotion', + 'usage' = 'usage', 'bill' = 'bill', - 'price' = 'price', - 'pay' = 'pay', 'inform' = 'inform', 'apikey' = 'apikey', 'loginout' = 'loginout' @@ -45,27 +43,18 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { ...(feConfigs?.isPlus ? [ { - icon: 'support/bill/billRecordLight', + icon: 'support/usage/usageRecordLight', label: t('user.Usage Record'), - id: TabEnum.bill + id: TabEnum.usage } ] : []), ...(feConfigs?.show_pay && userInfo?.team.canWrite ? [ { - icon: 'support/pay/payRecordLight', - label: t('user.Recharge Record'), - id: TabEnum.pay - } - ] - : []), - ...(feConfigs?.show_pay - ? [ - { - icon: 'support/pay/priceLight', - label: t('support.user.Price'), - id: TabEnum.price + icon: 'support/bill/payRecordLight', + label: t('support.wallet.Bills'), + id: TabEnum.bill } ] : []), @@ -108,11 +97,6 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { const { openConfirm, ConfirmModal } = useConfirm({ content: '确认退出登录?' }); - const { - isOpen: isOpenPriceBox, - onOpen: onOpenPriceBox, - onClose: onClosePriceBox - } = useDisclosure(); const router = useRouter(); const theme = useTheme(); @@ -124,8 +108,6 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { setUserInfo(null); router.replace('/login'); })(); - } else if (tab === TabEnum.price) { - onOpenPriceBox(); } else { router.replace({ query: { @@ -134,7 +116,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { }); } }, - [onOpenPriceBox, openConfirm, router, setUserInfo] + [openConfirm, router, setUserInfo] ); return ( @@ -178,16 +160,14 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { {currentTab === TabEnum.info && } {currentTab === TabEnum.promotion && } + {currentTab === TabEnum.usage && } {currentTab === TabEnum.bill && } - {currentTab === TabEnum.pay && } {currentTab === TabEnum.inform && } {currentTab === TabEnum.apikey && }
- - {isOpenPriceBox && } ); }; diff --git a/projects/app/src/pages/api/admin/initv468.ts b/projects/app/src/pages/api/admin/initv468.ts new file mode 100644 index 000000000..02caa11f1 --- /dev/null +++ b/projects/app/src/pages/api/admin/initv468.ts @@ -0,0 +1,99 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { PgClient } from '@fastgpt/service/common/vectorStore/pg'; +import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants'; +import { MongoImage } from '@fastgpt/service/common/file/image/schema'; +import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type'; +import { delay } from '@fastgpt/global/common/system/utils'; +import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; +import { getNanoid } from '@fastgpt/global/common/string/tools'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { ModuleItemType } from '@fastgpt/global/core/module/type'; +import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; + +let success = 0; +let deleteImg = 0; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + // 设置所有app为 inited = false + const result = await MongoApp.updateMany({}, { $set: { inited: false } }); + console.log(result); + + await initApp(); + + jsonRes(res, { + message: 'success' + }); + } catch (error) { + console.log(error); + + jsonRes(res, { + code: 500, + error + }); + } +} + +const systemKeys: string[] = [ + ModuleInputKeyEnum.switch, + ModuleInputKeyEnum.httpMethod, + ModuleInputKeyEnum.httpReqUrl, + ModuleInputKeyEnum.httpHeaders, + DYNAMIC_INPUT_KEY, + ModuleInputKeyEnum.addInputParam +]; +const initApp = async (): Promise => { + const app = await MongoApp.findOne({ inited: false }).sort({ updateTime: -1 }); + if (!app) { + return; + } + + try { + const modules = JSON.parse(JSON.stringify(app.modules)) as ModuleItemType[]; + let update = false; + // 找到http模块 + modules.forEach((module) => { + if (module.flowType === 'httpRequest') { + const method = module.inputs.find((input) => input.key === ModuleInputKeyEnum.httpMethod); + if (method?.value === 'POST') { + module.inputs.forEach((input) => { + // 更新非系统字段的key + if (!systemKeys.includes(input.key)) { + // 更新output的target + modules.forEach((item) => { + item.outputs.forEach((output) => { + output.targets.forEach((target) => { + if (target.moduleId === module.moduleId && target.key === input.key) { + target.key = `data.${input.key}`; + } + }); + }); + }); + // 更新key + input.key = `data.${input.key}`; + update = true; + } + }); + } + } + }); + + if (update) { + console.log('update http app'); + app.modules = modules; + } + app.inited = true; + await app.save(); + + console.log(++success); + return initApp(); + } catch (error) { + console.log(error); + + await delay(1000); + return initApp(); + } +}; diff --git a/projects/app/src/pages/api/admin/initv469.ts b/projects/app/src/pages/api/admin/initv469.ts new file mode 100644 index 000000000..54bf086ca --- /dev/null +++ b/projects/app/src/pages/api/admin/initv469.ts @@ -0,0 +1,35 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { MongoUsage } from '@fastgpt/service/support/wallet/usage/schema'; +import { connectionMongo } from '@fastgpt/service/common/mongo'; + +/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + await authCert({ req, authRoot: true }); + + // 检查 usage 是否有记录 + const totalUsages = await MongoUsage.countDocuments(); + if (totalUsages === 0) { + // 重命名 bills 集合成 usages + await connectionMongo.connection.db.renameCollection('bills', 'usages', { + // 强制 + dropTarget: true + }); + } + + jsonRes(res, { + message: 'success' + }); + } catch (error) { + console.log(error); + + jsonRes(res, { + code: 500, + error + }); + } +} diff --git a/projects/app/src/pages/api/common/file/upload.ts b/projects/app/src/pages/api/common/file/upload.ts index 3f3d135e5..efa9b2ad7 100644 --- a/projects/app/src/pages/api/common/file/upload.ts +++ b/projects/app/src/pages/api/common/file/upload.ts @@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< let filePaths: string[] = []; try { - const { userId, teamId, tmbId } = await authCert({ req, authToken: true }); + const { teamId, tmbId } = await authCert({ req, authToken: true }); const { file, bucketName, metadata } = await upload.doUpload(req, res); diff --git a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts index 722c4983d..a51329085 100644 --- a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts +++ b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts @@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d'; -import { pushQuestionGuideBill } from '@/service/support/wallet/bill/push'; +import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push'; import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide'; import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common'; @@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const qgModel = global.llmModels[0]; - const { result, inputTokens, outputTokens } = await createQuestionGuide({ + const { result, charsLength } = await createQuestionGuide({ messages, model: qgModel.model }); @@ -28,9 +28,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< data: result }); - pushQuestionGuideBill({ - inputTokens, - outputTokens, + pushQuestionGuideUsage({ + charsLength, teamId, tmbId }); diff --git a/projects/app/src/pages/api/core/app/create.ts b/projects/app/src/pages/api/core/app/create.ts index 26e3bfe24..dadfbedb7 100644 --- a/projects/app/src/pages/api/core/app/create.ts +++ b/projects/app/src/pages/api/core/app/create.ts @@ -6,6 +6,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants'; +import { checkTeamAppLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -25,12 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true }); // 上限校验 - const authCount = await MongoApp.countDocuments({ - teamId - }); - if (authCount >= 50) { - throw new Error('每个团队上限 50 个应用'); - } + await checkTeamAppLimit(teamId); // 创建模型 const response = await MongoApp.create({ diff --git a/projects/app/src/pages/api/core/app/data/totalUsage.ts b/projects/app/src/pages/api/core/app/data/totalUsage.ts deleted file mode 100644 index 137c2edaa..000000000 --- a/projects/app/src/pages/api/core/app/data/totalUsage.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { Types } from '@fastgpt/service/common/mongo'; -import { MongoBill } from '@fastgpt/service/support/wallet/bill/schema'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - const { appId, start, end } = req.body as { appId: string; start: number; end: number }; - const { userId } = await authCert({ req, authToken: true }); - - const result = await MongoBill.aggregate([ - { - $match: { - appId: new Types.ObjectId(appId), - userId: new Types.ObjectId(userId), - time: { $gte: new Date(start) } - } - }, - { - $group: { - _id: { - year: { $year: '$time' }, - month: { $month: '$time' }, - day: { $dayOfMonth: '$time' } - }, - total: { $sum: '$total' } - } - }, - { - $project: { - _id: 0, - date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } }, - total: 1 - } - }, - { $sort: { date: 1 } } - ]); - - jsonRes(res, { - data: result - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts index 3a23b4842..320a12b69 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts @@ -92,7 +92,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] { }, { key: 'model', - type: 'selectChatModel', + type: 'selectLLMModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', @@ -471,7 +471,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] { }, { key: 'model', - type: 'selectChatModel', + type: 'selectLLMModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts index 8b9a18263..9f4c706cb 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts @@ -88,7 +88,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { }, { key: 'model', - type: 'selectChatModel', + type: 'selectLLMModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', @@ -498,7 +498,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { }, { key: 'model', - type: 'selectChatModel', + type: 'selectLLMModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', diff --git a/projects/app/src/pages/api/core/app/update.ts b/projects/app/src/pages/api/core/app/update.ts index d8c00fb3f..01498ab5f 100644 --- a/projects/app/src/pages/api/core/app/update.ts +++ b/projects/app/src/pages/api/core/app/update.ts @@ -12,7 +12,7 @@ import { getLLMModel } from '@/service/core/ai/model'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - const { name, avatar, type, simpleTemplateId, intro, modules, permission } = + const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } = req.body as AppUpdateParams; const { appId } = req.query as { appId: string }; @@ -65,6 +65,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< avatar, intro, permission, + teamTags: teamTags, ...(modules && { modules }) diff --git a/projects/app/src/pages/api/core/app/updateTeamTasg.ts b/projects/app/src/pages/api/core/app/updateTeamTasg.ts new file mode 100644 index 000000000..8b3a750b9 --- /dev/null +++ b/projects/app/src/pages/api/core/app/updateTeamTasg.ts @@ -0,0 +1,82 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import type { AppUpdateParams } from '@fastgpt/global/core/app/api'; +import { authApp } from '@fastgpt/service/support/permission/auth/app'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { getLLMModel } from '@/service/core/ai/model'; + +/* 获取我的模型 */ +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } = + req.body as AppUpdateParams; + const { appId } = req.query as { appId: string }; + + if (!appId) { + throw new Error('appId is empty'); + } + + // 凭证校验 + await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' }); + + // check modules + // 1. dataset search limit, less than model quoteMaxToken + if (modules) { + let maxTokens = 3000; + + modules.forEach((item) => { + if (item.flowType === FlowNodeTypeEnum.chatNode) { + const model = + item.inputs.find((item) => item.key === ModuleInputKeyEnum.aiModel)?.value || ''; + const chatModel = getLLMModel(model); + const quoteMaxToken = chatModel.quoteMaxToken || 3000; + + maxTokens = Math.max(maxTokens, quoteMaxToken); + } + }); + + modules.forEach((item) => { + if (item.flowType === FlowNodeTypeEnum.datasetSearchNode) { + item.inputs.forEach((input) => { + if (input.key === ModuleInputKeyEnum.datasetMaxTokens) { + const val = input.value as number; + if (val > maxTokens) { + input.value = maxTokens; + } + } + }); + } + }); + } + + // 更新模型 + await MongoApp.findOneAndUpdate( + { + _id: appId + }, + { + name, + type, + simpleTemplateId, + avatar, + intro, + permission, + teamTags: teamTags, + ...(modules && { + modules + }) + } + ); + + jsonRes(res); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts index f9b7aeddc..bee667a46 100644 --- a/projects/app/src/pages/api/core/chat/chatTest.ts +++ b/projects/app/src/pages/api/core/chat/chatTest.ts @@ -4,13 +4,13 @@ import { sseErrRes } from '@fastgpt/service/common/response'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { responseWrite } from '@fastgpt/service/common/response'; import type { ModuleItemType } from '@fastgpt/global/core/module/type.d'; -import { pushChatBill } from '@/service/support/wallet/bill/push'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { pushChatUsage } from '@/service/support/wallet/usage/push'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import { authApp } from '@fastgpt/service/support/permission/auth/app'; import { dispatchModules } from '@/service/moduleDispatch'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller'; +import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; export type Props = { history: ChatItemType[]; @@ -50,13 +50,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ]); // auth balance - const user = await getUserAndAuthBalance({ - tmbId, - minBalance: 0 - }); + const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId); /* start process */ - const { responseData } = await dispatchModules({ + const { responseData, moduleDispatchBills } = await dispatchModules({ res, mode: 'test', teamId, @@ -85,13 +82,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); res.end(); - pushChatBill({ + pushChatUsage({ appName, appId, teamId, tmbId, - source: BillSourceEnum.fastgpt, - response: responseData + source: UsageSourceEnum.fastgpt, + moduleDispatchBills }); } catch (err: any) { res.status(500); diff --git a/projects/app/src/pages/api/core/chat/clearHistories.ts b/projects/app/src/pages/api/core/chat/clearHistories.ts index 99947245f..8a5424053 100644 --- a/projects/app/src/pages/api/core/chat/clearHistories.ts +++ b/projects/app/src/pages/api/core/chat/clearHistories.ts @@ -14,10 +14,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await connectToDatabase(); const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps; + let chatAppId = appId; + const match = await (async () => { if (shareId && outLinkUid) { - const { uid } = await authOutLink({ shareId, outLinkUid }); + const { appId, uid } = await authOutLink({ shareId, outLinkUid }); + chatAppId = appId; return { shareId, outLinkUid: uid @@ -41,11 +44,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const idList = list.map((item) => item.chatId); await MongoChatItem.deleteMany({ - appId, + appId: chatAppId, chatId: { $in: idList } }); await MongoChat.deleteMany({ - appId, + appId: chatAppId, chatId: { $in: idList } }); diff --git a/projects/app/src/pages/api/core/chat/getHistories.ts b/projects/app/src/pages/api/core/chat/getHistories.ts index c81f8cae6..97ca87fc6 100644 --- a/projects/app/src/pages/api/core/chat/getHistories.ts +++ b/projects/app/src/pages/api/core/chat/getHistories.ts @@ -28,6 +28,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } }; } + if (appId && outLinkUid) { + return { + shareId, + outLinkUid: outLinkUid, + source: ChatSourceEnum.team + }; + } if (appId) { const { tmbId } = await authCert({ req, authToken: true }); return { @@ -36,6 +43,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) source: ChatSourceEnum.online }; } + return Promise.reject('Params are error'); })(); diff --git a/projects/app/src/pages/api/core/chat/item/getSpeech.ts b/projects/app/src/pages/api/core/chat/item/getSpeech.ts index 17d9a647b..eb7db258c 100644 --- a/projects/app/src/pages/api/core/chat/item/getSpeech.ts +++ b/projects/app/src/pages/api/core/chat/item/getSpeech.ts @@ -3,9 +3,9 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { GetChatSpeechProps } from '@/global/core/chat/api.d'; import { text2Speech } from '@fastgpt/service/core/ai/audio/speech'; -import { pushAudioSpeechBill } from '@/service/support/wallet/bill/push'; +import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push'; import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common'; -import { authType2BillSource } from '@/service/support/wallet/bill/utils'; +import { authType2UsageSource } from '@/service/support/wallet/usage/utils'; import { getAudioSpeechModel } from '@/service/core/ai/model'; import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema'; @@ -54,12 +54,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) speed: ttsConfig.speed, onSuccess: async ({ model, buffer }) => { try { - pushAudioSpeechBill({ + pushAudioSpeechUsage({ model: model, charsLength: input.length, tmbId, teamId, - source: authType2BillSource({ authType }) + source: authType2UsageSource({ authType }) }); await MongoTTSBuffer.create({ diff --git a/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts b/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts new file mode 100644 index 000000000..224d9a074 --- /dev/null +++ b/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import type { chatByTeamProps } from '@/global/core/chat/api.d'; +import axios from 'axios'; +import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; +import { getChatItems } from '@fastgpt/service/core/chat/controller'; +import { selectShareResponse } from '@/utils/service/core/chat'; +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + + let { teamId, appId, outLinkUid } = req.query as chatByTeamProps; + + const history = await MongoChatItem.find({ + appId: appId, + outLinkUid: outLinkUid, + teamId: teamId + }); + + jsonRes(res, { + data: history + }); + } catch (err) { + jsonRes(res, { + code: 500, + data: req.query, + error: err + }); + } +} + +export const config = { + api: { + responseLimit: '10mb' + } +}; diff --git a/projects/app/src/pages/api/core/chat/team/init.ts b/projects/app/src/pages/api/core/chat/team/init.ts new file mode 100644 index 000000000..47b5f0e6f --- /dev/null +++ b/projects/app/src/pages/api/core/chat/team/init.ts @@ -0,0 +1,91 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { getGuideModule } from '@fastgpt/global/core/module/utils'; +import { getChatModelNameListByModules } from '@/service/core/app/module'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; +import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d'; +import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { getChatItems } from '@fastgpt/service/core/chat/controller'; +import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + + let { appId, chatId, outLinkUid } = req.query as { + chatId?: string; + appId?: string; + outLinkUid?: string; + }; + + if (!appId) { + return jsonRes(res, { + code: 501, + message: "You don't have an app yet" + }); + } + + // auth app permission + const [chat, app] = await Promise.all([ + // authApp({ + // req, + // authToken: false, + // appId, + // per: 'r' + // }), + chatId ? MongoChat.findOne({ appId, chatId }) : undefined, + MongoApp.findById(appId).lean() + ]); + if (!app) { + throw new Error(AppErrEnum.unExist); + } + + // auth chat permission + // if (chat && chat.outLinkUid !== outLinkUid) { + // throw new Error(ChatErrEnum.unAuthChat); + // } + // // auth chat permission + // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) { + // throw new Error(ChatErrEnum.unAuthChat); + // } + + // get app and history + const { history } = await getChatItems({ + appId, + chatId, + limit: 30, + field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ModuleOutputKeyEnum.responseData}` + }); + + jsonRes(res, { + data: { + chatId, + appId, + title: chat?.title || '新对话', + userAvatar: undefined, + variables: chat?.variables || {}, + history, + app: { + userGuideModule: getGuideModule(app.modules), + chatModels: getChatModelNameListByModules(app.modules), + name: app.name, + avatar: app.avatar, + intro: app.intro + } + } + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} + +export const config = { + api: { + responseLimit: '10mb' + } +}; diff --git a/projects/app/src/pages/api/core/chat/teamInit.ts b/projects/app/src/pages/api/core/chat/teamInit.ts new file mode 100644 index 000000000..a3087e15c --- /dev/null +++ b/projects/app/src/pages/api/core/chat/teamInit.ts @@ -0,0 +1,81 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { authApp } from '@fastgpt/service/support/permission/auth/app'; +import { getGuideModule } from '@fastgpt/global/core/module/utils'; +import { getChatModelNameListByModules } from '@/service/core/app/module'; +import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; +import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d'; +import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; +import { getChatItems } from '@fastgpt/service/core/chat/controller'; +import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + + let { appId, chatId, loadCustomFeedbacks } = req.query as InitChatProps; + + if (!appId) { + return jsonRes(res, { + code: 501, + message: "You don't have an app yet" + }); + } + + // auth app permission + const [{ app, tmbId }, chat] = await Promise.all([ + authApp({ + req, + authToken: true, + appId, + per: 'r' + }), + chatId ? MongoChat.findOne({ appId, chatId }) : undefined + ]); + + // // auth chat permission + // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) { + // throw new Error(ChatErrEnum.unAuthChat); + // } + + // get app and history + const { history } = await getChatItems({ + appId, + chatId, + limit: 30, + field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ + ModuleOutputKeyEnum.responseData + } ${loadCustomFeedbacks ? 'customFeedbacks' : ''}` + }); + + jsonRes(res, { + data: { + chatId, + appId, + title: chat?.title || '新对话', + userAvatar: undefined, + variables: chat?.variables || {}, + history, + app: { + userGuideModule: getGuideModule(app.modules), + chatModels: getChatModelNameListByModules(app.modules), + name: app.name, + avatar: app.avatar, + intro: app.intro + } + } + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} + +export const config = { + api: { + responseLimit: '10mb' + } +}; diff --git a/projects/app/src/pages/api/core/chat/updateHistory.ts b/projects/app/src/pages/api/core/chat/updateHistory.ts index 634336c9a..634604894 100644 --- a/projects/app/src/pages/api/core/chat/updateHistory.ts +++ b/projects/app/src/pages/api/core/chat/updateHistory.ts @@ -24,6 +24,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await MongoChat.findOneAndUpdate( { appId, chatId }, { + updateTime: new Date(), ...(customTitle !== undefined && { customTitle }), ...(top !== undefined && { top }) } diff --git a/projects/app/src/pages/api/core/dataset/collection/create/link.ts b/projects/app/src/pages/api/core/dataset/collection/create/link.ts index 15f0ac26a..317bd3fa5 100644 --- a/projects/app/src/pages/api/core/dataset/collection/create/link.ts +++ b/projects/app/src/pages/api/core/dataset/collection/create/link.ts @@ -11,13 +11,12 @@ import { TrainingModeEnum, DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; -import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; -import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; import { reloadCollectionChunks } from '@fastgpt/service/core/dataset/collection/utils'; -import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -43,8 +42,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 1. check dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(trainingType, new Array(10)), - standardPlans: getStandardSubPlan() + insertLen: predictDataLimitLength(trainingType, new Array(10)) }); const { _id: collectionId } = await mongoSessionRun(async (session) => { @@ -66,11 +64,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }); // 3. create bill and start sync - const { billId } = await createTrainingBill({ + const { billId } = await createTrainingUsage({ teamId, tmbId, appName: 'core.dataset.collection.Sync Collection', - billSource: BillSourceEnum.training, + billSource: UsageSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel).name, agentModel: getLLMModel(dataset.agentModel).name, session diff --git a/projects/app/src/pages/api/core/dataset/collection/create/text.ts b/projects/app/src/pages/api/core/dataset/collection/create/text.ts index b24a1724c..891baf297 100644 --- a/projects/app/src/pages/api/core/dataset/collection/create/text.ts +++ b/projects/app/src/pages/api/core/dataset/collection/create/text.ts @@ -12,14 +12,13 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; -import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller'; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; -import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -53,8 +52,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 2. check dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(trainingType, chunks), - standardPlans: getStandardSubPlan() + insertLen: predictDataLimitLength(trainingType, chunks) }); // 3. create collection and training bill @@ -74,11 +72,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< hashRawText: hashStr(text), rawTextLength: text.length }), - createTrainingBill({ + createTrainingUsage({ teamId, tmbId, appName: name, - billSource: BillSourceEnum.training, + billSource: UsageSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel)?.name, agentModel: getLLMModel(dataset.agentModel)?.name }) diff --git a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts index 7c0104c99..0e93e43bd 100644 --- a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts +++ b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts @@ -12,8 +12,8 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; -import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; @@ -56,11 +56,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await mongoSessionRun(async (session) => { // create training bill - const { billId } = await createTrainingBill({ + const { billId } = await createTrainingUsage({ teamId: collection.teamId, tmbId, appName: 'core.dataset.collection.Sync Collection', - billSource: BillSourceEnum.training, + billSource: UsageSourceEnum.training, vectorModel: vectorModelData.name, agentModel: agentModelData.name, session diff --git a/projects/app/src/pages/api/core/dataset/create.ts b/projects/app/src/pages/api/core/dataset/create.ts index c1dccb8c1..eef14e167 100644 --- a/projects/app/src/pages/api/core/dataset/create.ts +++ b/projects/app/src/pages/api/core/dataset/create.ts @@ -7,6 +7,7 @@ import { createDefaultCollection } from '@fastgpt/service/core/dataset/collectio import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { getLLMModel, getVectorModel, getDatasetModel } from '@/service/core/ai/model'; +import { checkTeamDatasetLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -31,13 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } // check limit - const authCount = await MongoDataset.countDocuments({ - teamId, - type: DatasetTypeEnum.dataset - }); - if (authCount >= 50) { - throw new Error('每个团队上限 50 个知识库'); - } + await checkTeamDatasetLimit(teamId); const { _id } = await MongoDataset.create({ name, diff --git a/projects/app/src/pages/api/core/dataset/data/delete.ts b/projects/app/src/pages/api/core/dataset/data/delete.ts index b945d3e56..708f21ada 100644 --- a/projects/app/src/pages/api/core/dataset/data/delete.ts +++ b/projects/app/src/pages/api/core/dataset/data/delete.ts @@ -3,8 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { withNextCors } from '@fastgpt/service/common/middle/cors'; import { connectToDatabase } from '@/service/mongo'; import { authDatasetData } from '@/service/support/permission/auth/dataset'; -import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; -import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller'; +import { deleteDatasetData } from '@/service/core/dataset/data/controller'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -26,19 +25,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex per: 'w' }); - // update mongo data update time - await MongoDatasetData.findByIdAndUpdate(dataId, { - updateTime: new Date() - }); - - // delete vector data - await deleteDatasetDataVector({ - teamId, - idList: datasetData.indexes.map((item) => item.dataId) - }); - - // delete mongo data - await MongoDatasetData.findByIdAndDelete(dataId); + await deleteDatasetData(datasetData); jsonRes(res, { data: 'success' diff --git a/projects/app/src/pages/api/core/dataset/data/insertData.ts b/projects/app/src/pages/api/core/dataset/data/insertData.ts index a8ec06bf1..7ca9773ea 100644 --- a/projects/app/src/pages/api/core/dataset/data/insertData.ts +++ b/projects/app/src/pages/api/core/dataset/data/insertData.ts @@ -12,12 +12,10 @@ import { hasSameValue } from '@/service/core/dataset/data/utils'; import { insertData2Dataset } from '@/service/core/dataset/data/controller'; import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset'; import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller'; -import { authTeamBalance } from '@/service/support/permission/auth/bill'; -import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; +import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; import { InsertOneDatasetDataProps } from '@/global/core/dataset/api'; import { simpleText } from '@fastgpt/global/common/string/tools'; -import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; -import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -43,8 +41,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex await checkDatasetLimit({ teamId, - insertLen: 1, - standardPlans: getStandardSubPlan() + insertLen: 1 }); // auth collection and get dataset @@ -52,7 +49,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex { datasetId: { _id: datasetId, vectorModel } } - ] = await Promise.all([getCollectionWithDataset(collectionId), authTeamBalance(teamId)]); + ] = await Promise.all([getCollectionWithDataset(collectionId)]); // format data const formatQ = simpleText(q); @@ -90,7 +87,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex indexes: formatIndexes }); - pushGenerateVectorBill({ + pushGenerateVectorUsage({ teamId, tmbId, charsLength, diff --git a/projects/app/src/pages/api/core/dataset/data/pushData.ts b/projects/app/src/pages/api/core/dataset/data/pushData.ts index 8e50ef333..476cd4532 100644 --- a/projects/app/src/pages/api/core/dataset/data/pushData.ts +++ b/projects/app/src/pages/api/core/dataset/data/pushData.ts @@ -8,10 +8,9 @@ import type { PushDatasetDataResponse } from '@fastgpt/global/core/dataset/api.d'; import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset'; -import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller'; -import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -38,8 +37,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex // auth dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(collection.trainingType, data), - standardPlans: getStandardSubPlan() + insertLen: predictDataLimitLength(collection.trainingType, data) }); jsonRes(res, { diff --git a/projects/app/src/pages/api/core/dataset/data/update.ts b/projects/app/src/pages/api/core/dataset/data/update.ts index 7f7102d4b..4cd8f5bfe 100644 --- a/projects/app/src/pages/api/core/dataset/data/update.ts +++ b/projects/app/src/pages/api/core/dataset/data/update.ts @@ -4,9 +4,9 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors'; import { connectToDatabase } from '@/service/mongo'; import { updateData2Dataset } from '@/service/core/dataset/data/controller'; import { authDatasetData } from '@/service/support/permission/auth/dataset'; -import { authTeamBalance } from '@/service/support/permission/auth/bill'; -import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; +import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; import { UpdateDatasetDataProps } from '@/global/core/dataset/api'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -29,7 +29,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); // auth team balance - await authTeamBalance(teamId); + await checkDatasetLimit({ + teamId, + insertLen: 1 + }); const { charsLength } = await updateData2Dataset({ dataId: id, @@ -39,7 +42,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex model: vectorModel }); - pushGenerateVectorBill({ + pushGenerateVectorUsage({ teamId, tmbId, charsLength, diff --git a/projects/app/src/pages/api/core/dataset/searchTest.ts b/projects/app/src/pages/api/core/dataset/searchTest.ts index 6358ba381..cce91385c 100644 --- a/projects/app/src/pages/api/core/dataset/searchTest.ts +++ b/projects/app/src/pages/api/core/dataset/searchTest.ts @@ -4,14 +4,16 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors'; import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d'; import { connectToDatabase } from '@/service/mongo'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; -import { authTeamBalance } from '@/service/support/permission/auth/bill'; -import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; +import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; import { searchDatasetData } from '@/service/core/dataset/data/controller'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; import { getLLMModel } from '@/service/core/ai/model'; -import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension'; import { datasetSearchQueryExtension } from '@fastgpt/service/core/dataset/search/utils'; +import { + checkTeamAIPoints, + checkTeamReRankPermission +} from '@/service/support/permission/teamLimit'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -43,7 +45,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex per: 'r' }); // auth balance - await authTeamBalance(teamId); + await checkTeamAIPoints(teamId); // query extension const extensionModel = @@ -65,28 +67,27 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex similarity, datasetIds: [datasetId], searchMode, - usingReRank + usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)) }); // push bill - const { total } = pushGenerateVectorBill({ + const { totalPoints } = pushGenerateVectorUsage({ teamId, tmbId, charsLength, model: dataset.vectorModel, - source: apikey ? BillSourceEnum.api : BillSourceEnum.fastgpt, + source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt, ...(aiExtensionResult && extensionModel && { extensionModel: extensionModel.name, - extensionInputTokens: aiExtensionResult.inputTokens, - extensionOutputTokens: aiExtensionResult.outputTokens + extensionCharsLength: aiExtensionResult.charsLength }) }); if (apikey) { updateApiKeyUsage({ apikey, - usage: total + totalPoints: totalPoints }); } diff --git a/projects/app/src/pages/api/core/plugin/create.ts b/projects/app/src/pages/api/core/plugin/create.ts index c0f4ab035..b5fe5c26d 100644 --- a/projects/app/src/pages/api/core/plugin/create.ts +++ b/projects/app/src/pages/api/core/plugin/create.ts @@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo'; import type { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { MongoPlugin } from '@fastgpt/service/core/plugin/schema'; +import { checkTeamPluginLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -11,6 +12,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true }); const body = req.body as CreateOnePluginParams; + await checkTeamPluginLimit(teamId); + const { _id } = await MongoPlugin.create({ ...body, teamId, diff --git a/projects/app/src/pages/api/plugins/textEditor/index.ts b/projects/app/src/pages/api/plugins/textEditor/index.ts index c7aa2c06a..35ad8de80 100644 --- a/projects/app/src/pages/api/plugins/textEditor/index.ts +++ b/projects/app/src/pages/api/plugins/textEditor/index.ts @@ -34,7 +34,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }); const textResult = replaceVariable(text, obj); - res.json({ text: textResult }); diff --git a/projects/app/src/pages/api/support/user/account/update.ts b/projects/app/src/pages/api/support/user/account/update.ts index 7b29daad3..af55c441d 100644 --- a/projects/app/src/pages/api/support/user/account/update.ts +++ b/projects/app/src/pages/api/support/user/account/update.ts @@ -5,6 +5,7 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { UserUpdateParams } from '@/types/user'; import { getAIApi, openaiBaseUrl } from '@fastgpt/service/core/ai/config'; import { connectToDatabase } from '@/service/mongo'; +import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; /* update user info */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -12,8 +13,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await connectToDatabase(); const { avatar, timezone, openaiAccount } = req.body as UserUpdateParams; - const { userId } = await authCert({ req, authToken: true }); - + const { tmbId } = await authCert({ req, authToken: true }); + const tmb = await MongoTeamMember.findById(tmbId); + if (!tmb) { + throw new Error('can not find it'); + } + const userId = tmb.userId; // auth key if (openaiAccount?.key) { console.log('auth user openai key', openaiAccount?.key); diff --git a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts index ad4eec2bf..86712d749 100644 --- a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts +++ b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts @@ -3,6 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { MongoUser } from '@fastgpt/service/support/user/schema'; import { connectToDatabase } from '@/service/mongo'; +import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -13,8 +14,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< throw new Error('Params is missing'); } - const { userId } = await authCert({ req, authToken: true }); - + const { tmbId } = await authCert({ req, authToken: true }); + const tmb = await MongoTeamMember.findById(tmbId); + if (!tmb) { + throw new Error('can not find it'); + } + const userId = tmb.userId; // auth old password const user = await MongoUser.findOne({ _id: userId, diff --git a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts index 4d22ffec3..e7f9856f2 100644 --- a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts +++ b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts @@ -2,8 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; -import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; +import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -23,8 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await checkDatasetLimit({ teamId, - insertLen: numberSize, - standardPlans: getStandardSubPlan() + insertLen: numberSize }); jsonRes(res); diff --git a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts b/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts index 83eb283fd..074bedf7e 100644 --- a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts +++ b/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts @@ -2,22 +2,21 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { getTeamSubPlanStatus } from '@fastgpt/service/support/wallet/sub/utils'; +import { getTeamSubPlans } from '@fastgpt/service/support/wallet/sub/utils'; import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; -import { FeTeamSubType } from '@fastgpt/global/support/wallet/sub/type'; +import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - // 凭证校验 const { teamId } = await authCert({ req, authToken: true }); - jsonRes(res, { - data: await getTeamSubPlanStatus({ + jsonRes(res, { + data: await getTeamSubPlans({ teamId, standardPlans: getStandardSubPlan() }) diff --git a/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts b/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts similarity index 68% rename from projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts rename to projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts index fec583d69..2cbc3d63b 100644 --- a/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts +++ b/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts @@ -1,16 +1,16 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; -import { CreateTrainingBillProps } from '@fastgpt/global/support/wallet/bill/api.d'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; -import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; +import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - const { name, datasetId } = req.body as CreateTrainingBillProps; + const { name, datasetId } = req.body as CreateTrainingUsageProps; const { teamId, tmbId, dataset } = await authDataset({ req, @@ -20,11 +20,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) per: 'w' }); - const { billId } = await createTrainingBill({ + const { billId } = await createTrainingUsage({ teamId, tmbId, appName: name, - billSource: BillSourceEnum.training, + billSource: UsageSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel).name, agentModel: getLLMModel(dataset.agentModel).name }); diff --git a/projects/app/src/pages/api/v1/audio/transcriptions.ts b/projects/app/src/pages/api/v1/audio/transcriptions.ts index 189ab13c4..1c6997d51 100644 --- a/projects/app/src/pages/api/v1/audio/transcriptions.ts +++ b/projects/app/src/pages/api/v1/audio/transcriptions.ts @@ -6,7 +6,7 @@ import { getUploadModel } from '@fastgpt/service/common/file/multer'; import { removeFilesByPaths } from '@fastgpt/service/common/file/utils'; import fs from 'fs'; import { getAIApi } from '@fastgpt/service/core/ai/config'; -import { pushWhisperBill } from '@/service/support/wallet/bill/push'; +import { pushWhisperUsage } from '@/service/support/wallet/usage/push'; const upload = getUploadModel({ maxSize: 2 @@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex model: global.whisperModel.model }); - pushWhisperBill({ + pushWhisperUsage({ teamId, tmbId, duration diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index f703072ab..9f06f9bd3 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -13,16 +13,16 @@ import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt'; import { getChatItems } from '@fastgpt/service/core/chat/controller'; import { saveChat } from '@/service/utils/chat/saveChat'; import { responseWrite } from '@fastgpt/service/common/response'; -import { pushChatBill } from '@/service/support/wallet/bill/push'; +import { pushChatUsage } from '@/service/support/wallet/usage/push'; import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink'; -import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; +import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; import requestIp from 'request-ip'; -import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools'; +import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; import { selectShareResponse } from '@/utils/service/core/chat'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; import { connectToDatabase } from '@/service/mongo'; -import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller'; +import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import { autChatCrud } from '@/service/support/permission/auth/chat'; @@ -35,9 +35,14 @@ type FastGptShareChatProps = { shareId?: string; outLinkUid?: string; }; +type FastGptTeamShareChatProps = { + teamId?: string; + outLinkUid?: string; +}; export type Props = ChatCompletionCreateParams & FastGptWebChatProps & - FastGptShareChatProps & { + FastGptShareChatProps & + FastGptTeamShareChatProps & { messages: ChatMessageItemType[]; stream?: boolean; detail?: boolean; @@ -60,6 +65,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex const { chatId, appId, + teamId, shareId, outLinkUid, stream = false, @@ -97,90 +103,96 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } /* auth app permission */ - const { user, app, responseDetail, authType, apikey, canWrite, uid } = await (async () => { - if (shareId && outLinkUid) { - const { user, appId, authType, responseDetail, uid } = await authOutLinkChatStart({ - shareId, - ip: originIp, - outLinkUid, - question: question.value - }); - const app = await MongoApp.findById(appId); + const { teamId, tmbId, user, app, responseDetail, authType, apikey, canWrite, outLinkUserId } = + await (async () => { + if (shareId && outLinkUid) { + const { teamId, tmbId, user, appId, authType, responseDetail, uid } = + await authOutLinkChatStart({ + shareId, + ip: originIp, + outLinkUid, + question: question.value + }); + const app = await MongoApp.findById(appId); - if (!app) { - return Promise.reject('app is empty'); + if (!app) { + return Promise.reject('app is empty'); + } + + return { + teamId, + tmbId, + user, + app, + responseDetail, + apikey: '', + authType, + canWrite: false, + outLinkUserId: uid + }; } - return { - user, - app, - responseDetail, - apikey: '', + const { + appId: apiKeyAppId, + teamId, + tmbId, authType, - canWrite: false, - uid - }; - } + apikey + } = await authCert({ + req, + authToken: true, + authApiKey: true + }); - const { - appId: apiKeyAppId, - tmbId, - authType, - apikey - } = await authCert({ - req, - authToken: true, - authApiKey: true - }); + const user = await getUserChatInfoAndAuthTeamPoints(tmbId); - const user = await getUserAndAuthBalance({ - tmbId, - minBalance: 0 - }); + // openapi key + if (authType === AuthUserTypeEnum.apikey) { + if (!apiKeyAppId) { + return Promise.reject( + 'Key is error. You need to use the app key rather than the account key.' + ); + } + const app = await MongoApp.findById(apiKeyAppId); - // openapi key - if (authType === AuthUserTypeEnum.apikey) { - if (!apiKeyAppId) { - return Promise.reject( - 'Key is error. You need to use the app key rather than the account key.' - ); + if (!app) { + return Promise.reject('app is empty'); + } + + return { + teamId, + tmbId, + user, + app, + responseDetail: detail, + apikey, + authType, + canWrite: true + }; } - const app = await MongoApp.findById(apiKeyAppId); - if (!app) { - return Promise.reject('app is empty'); + // token auth + if (!appId) { + return Promise.reject('appId is empty'); } + const { app, canWrite } = await authApp({ + req, + authToken: true, + appId, + per: 'r' + }); return { + teamId, + tmbId, user, app, responseDetail: detail, apikey, authType, - canWrite: true + canWrite: canWrite || false }; - } - - // token auth - if (!appId) { - return Promise.reject('appId is empty'); - } - const { app, canWrite } = await authApp({ - req, - authToken: true, - appId, - per: 'r' - }); - - return { - user, - app, - responseDetail: detail, - apikey, - authType, - canWrite: canWrite || false - }; - })(); + })(); // auth chat permission await autChatCrud({ @@ -201,16 +213,17 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex limit: 30, field: `dataId obj value` }); + const concatHistories = history.concat(chatMessages); const responseChatItemId: string | undefined = messages[messages.length - 1].dataId; /* start flow controller */ - const { responseData, answerText } = await dispatchModules({ + const { responseData, moduleDispatchBills, answerText } = await dispatchModules({ res, mode: 'chat', user, - teamId: String(user.team.teamId), - tmbId: String(user.team.tmbId), + teamId: String(teamId), + tmbId: String(tmbId), appId: String(app._id), chatId, responseChatItemId, @@ -223,18 +236,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex stream, detail }); + console.log('af'); // save chat if (chatId) { await saveChat({ chatId, appId: app._id, - teamId: user.team.teamId, - tmbId: user.team.tmbId, + teamId: teamId, + tmbId: tmbId, variables, - updateUseTime: !shareId && String(user.team.tmbId) === String(app.tmbId), // owner update use time + updateUseTime: !shareId && String(tmbId) === String(app.tmbId), // owner update use time shareId, - outLinkUid: uid, + outLinkUid: outLinkUserId, source: (() => { if (shareId) { return ChatSourceEnum.share; @@ -305,29 +319,29 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } // add record - const { total } = pushChatBill({ + const { totalPoints } = pushChatUsage({ appName: app.name, appId: app._id, - teamId: user.team.teamId, - tmbId: user.team.tmbId, - source: getBillSourceByAuthType({ shareId, authType }), - response: responseData + teamId: teamId, + tmbId: tmbId, + source: getUsageSourceByAuthType({ shareId, authType }), + moduleDispatchBills }); if (shareId) { - pushResult2Remote({ outLinkUid, shareId, responseData }); - updateOutLinkUsage({ + pushResult2Remote({ outLinkUid, shareId, appName: app.name, responseData }); + addOutLinkUsage({ shareId, - total + totalPoints }); } if (apikey) { updateApiKeyUsage({ apikey, - usage: total + totalPoints }); } - } catch (err: any) { + } catch (err) { if (stream) { sseErrRes(res, err); res.end(); diff --git a/projects/app/src/pages/api/v1/embeddings.ts b/projects/app/src/pages/api/v1/embeddings.ts index a363ecb78..3f82ecf87 100644 --- a/projects/app/src/pages/api/v1/embeddings.ts +++ b/projects/app/src/pages/api/v1/embeddings.ts @@ -2,13 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { withNextCors } from '@fastgpt/service/common/middle/cors'; -import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; +import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; import { connectToDatabase } from '@/service/mongo'; -import { authTeamBalance } from '@/service/support/permission/auth/bill'; import { getVectorsByText } from '@fastgpt/service/core/ai/embedding'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; -import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools'; +import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; import { getVectorModel } from '@/service/core/ai/model'; +import { checkTeamAIPoints } from '@/service/support/permission/teamLimit'; type Props = { input: string | string[]; @@ -34,7 +34,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex authApiKey: true }); - await authTeamBalance(teamId); + await checkTeamAIPoints(teamId); const { charsLength, vectors } = await getVectorsByText({ input: query, @@ -55,19 +55,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } }); - const { total } = pushGenerateVectorBill({ + const { totalPoints } = pushGenerateVectorUsage({ teamId, tmbId, charsLength, model, billId, - source: getBillSourceByAuthType({ authType }) + source: getUsageSourceByAuthType({ authType }) }); if (apikey) { updateApiKeyUsage({ apikey, - usage: total + totalPoints: totalPoints }); } } catch (err) { diff --git a/projects/app/src/pages/api/v1/rerank.ts b/projects/app/src/pages/api/v1/rerank.ts deleted file mode 100644 index c73ab0b6c..000000000 --- a/projects/app/src/pages/api/v1/rerank.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { withNextCors } from '@fastgpt/service/common/middle/cors'; -import { pushReRankBill } from '@/service/support/wallet/bill/push'; -import { connectToDatabase } from '@/service/mongo'; -import { authTeamBalance } from '@/service/support/permission/auth/bill'; -import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api'; -import { reRankRecall } from '@/service/core/ai/rerank'; -import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; - -export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { - let { query, inputs } = req.body as PostReRankProps; - try { - await connectToDatabase(); - const { teamId, tmbId, apikey } = await authCert({ - req, - authApiKey: true - }); - await authTeamBalance(teamId); - - // max 150 length - inputs = inputs.slice(0, 150); - - const result = await reRankRecall({ query, inputs }); - - const { total } = pushReRankBill({ - teamId, - tmbId, - source: 'api', - inputs - }); - - if (apikey) { - updateApiKeyUsage({ - apikey, - usage: total - }); - } - - jsonRes(res, { - data: result - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -}); diff --git a/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx b/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx deleted file mode 100644 index 8822483ef..000000000 --- a/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React, { useEffect, useMemo, useRef } from 'react'; -import * as echarts from 'echarts'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { getAppTotalUsage } from '@/web/core/app/api'; -import { useQuery } from '@tanstack/react-query'; -import dayjs from 'dayjs'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; -import Loading from '@/components/Loading'; -import { Box } from '@chakra-ui/react'; - -const map = { - blue: { - backgroundColor: { - type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, - colorStops: [ - { - offset: 0, - color: 'rgba(3, 190, 232, 0.42)' // 0% 处的颜色 - }, - { - offset: 1, - color: 'rgba(0, 182, 240, 0)' - } - ], - global: false // 缺省为 false - }, - lineColor: '#36ADEF' - }, - deepBlue: { - backgroundColor: { - type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, - colorStops: [ - { - offset: 0, - color: 'rgba(47, 112, 237, 0.42)' // 0% 处的颜色 - }, - { - offset: 1, - color: 'rgba(94, 159, 235, 0)' - } - ], - global: false - }, - lineColor: '#3293EC' - }, - purple: { - backgroundColor: { - type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, - colorStops: [ - { - offset: 0, - color: 'rgba(211, 190, 255, 0.42)' // 0% 处的颜色 - }, - { - offset: 1, - color: 'rgba(52, 60, 255, 0)' - } - ], - global: false // 缺省为 false - }, - lineColor: '#8172D8' - }, - green: { - backgroundColor: { - type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, - colorStops: [ - { - offset: 0, - color: 'rgba(4, 209, 148, 0.42)' // 0% 处的颜色 - }, - { - offset: 1, - color: 'rgba(19, 217, 181, 0)' - } - ], - global: false // 缺省为 false - }, - lineColor: '#00A9A6', - max: 100 - } -}; - -const TokenUsage = ({ appId }: { appId: string }) => { - const { screenWidth } = useSystemStore(); - - const Dom = useRef(null); - const myChart = useRef(); - const { data = [] } = useQuery(['init'], () => getAppTotalUsage({ appId })); - - const option = useMemo( - () => ({ - xAxis: { - type: 'category', - show: false, - boundaryGap: false, - data: data.map((item) => item.date) - }, - yAxis: { - type: 'value', - splitNumber: 3, - min: 0 - }, - grid: { - show: false, - left: 5, - right: 5, - top: 0, - bottom: 5 - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'line' - }, - formatter: (e: any[]) => { - const data = e[0]; - if (!data) return ''; - - return ` -
-
${dayjs(data.axisValue).format('YYYY/MM/DD')}
-
${formatStorePrice2Read(e[0]?.value || 0)}元
-
-`; - } - }, - series: [ - { - data: data.map((item) => item.total), - type: 'line', - showSymbol: true, - animationDuration: 1000, - animationEasingUpdate: 'linear', - areaStyle: { - color: map['blue'].backgroundColor - }, - lineStyle: { - width: '1', - color: map['blue'].lineColor - }, - itemStyle: { - width: 1.5, - color: map['blue'].lineColor - }, - emphasis: { - // highlight - disabled: true - } - } - ] - }), - [data] - ); - - // init chart - useEffect(() => { - if (!Dom.current || myChart?.current?.getOption()) return; - myChart.current = echarts.init(Dom.current); - myChart.current && myChart.current.setOption(option); - - setTimeout(() => { - myChart.current?.resize(); - }, 500); - }, []); - - // data changed, update - useEffect(() => { - if (!myChart.current || !myChart?.current?.getOption()) return; - myChart.current.setOption(option); - }, [data, option]); - - // resize chart - useEffect(() => { - if (!myChart.current || !myChart.current.getOption()) return; - myChart.current.resize(); - }, [screenWidth]); - - return ( - - - - ); -}; - -export default React.memo(TokenUsage); diff --git a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx index ee7980ea3..048028f33 100644 --- a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx +++ b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx @@ -36,7 +36,6 @@ import { useForm } from 'react-hook-form'; import { defaultOutLinkForm } from '@/constants/app'; import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d'; import { useRequest } from '@/web/common/hooks/useRequest'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant'; import { useTranslation } from 'next-i18next'; import { useToast } from '@fastgpt/web/hooks/useToast'; @@ -94,7 +93,7 @@ const Share = ({ appId }: { appId: string }) => { {t('common.Name')} - {t('common.Price used')} + {t('support.outlink.Usage points')} {t('core.app.share.Is response quote')} {feConfigs?.isPlus && ( <> @@ -112,11 +111,11 @@ const Share = ({ appId }: { appId: string }) => { {item.name} - {formatStorePrice2Read(item.total)} + {Math.round(item.usagePoints)} {feConfigs?.isPlus ? `${ - item.limit && item.limit.credit > -1 - ? ` / ¥${item.limit.credit}` + item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1 + ? ` / ${item.limit.maxUsagePoints}` : ` / ${t('common.Unlimited')}` }` : ''} @@ -315,15 +314,15 @@ function EditLinkModal({ - {t('common.Max credit')} - + {t('support.outlink.Max usage points')} + import('../InfoModal')); const AppCard = ({ appId }: { appId: string }) => { @@ -20,6 +22,7 @@ const AppCard = ({ appId }: { appId: string }) => { const { toast } = useToast(); const { appDetail } = useAppStore(); const [settingAppInfo, setSettingAppInfo] = useState(); + const [TeamTagsSet, setTeamTagsSet] = useState(); const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({ content: t('app.Confirm Del App Tip') @@ -123,6 +126,17 @@ const AppCard = ({ appId }: { appId: string }) => { > {t('core.app.navbar.Publish')} + {appDetail.isOwner && ( + + )} {appDetail.isOwner && ( + + + + + ); +}; +export default TagsEditModal; diff --git a/projects/app/src/pages/app/list/component/CreateModal.tsx b/projects/app/src/pages/app/list/component/CreateModal.tsx index e0b107357..fa3039bd6 100644 --- a/projects/app/src/pages/app/list/component/CreateModal.tsx +++ b/projects/app/src/pages/app/list/component/CreateModal.tsx @@ -8,8 +8,12 @@ import { Input, Grid, useTheme, - Card + Card, + Text, + HStack, + Tag } from '@chakra-ui/react'; +import { AddIcon } from '@chakra-ui/icons'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useForm } from 'react-hook-form'; import { compressImgFileAndUpload } from '@/web/common/file/controller'; diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 88db3bac8..ace56978c 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import { Box, Grid, Flex, IconButton, Button, useDisclosure } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import { useQuery } from '@tanstack/react-query'; @@ -8,7 +8,6 @@ import { useToast } from '@fastgpt/web/hooks/useToast'; import { useConfirm } from '@/web/common/hooks/useConfirm'; import { serviceSideProps } from '@/web/common/utils/i18n'; import { useTranslation } from 'next-i18next'; - import MyIcon from '@fastgpt/web/components/common/Icon'; import PageContainer from '@/components/PageContainer'; import Avatar from '@/components/Avatar'; @@ -24,6 +23,7 @@ const MyApps = () => { const router = useRouter(); const { userInfo } = useUserStore(); const { myApps, loadMyApps } = useAppStore(); + const [teamsTags, setTeamTags] = useState([]); const { openConfirm, ConfirmModal } = useConfirm({ title: '删除提示', content: '确认删除该应用所有信息?' @@ -65,11 +65,9 @@ const MyApps = () => { {t('app.My Apps')} - {userInfo?.team?.canWrite && ( - - )} + { ))} + {/* ( + + ) */} + {myApps.length === 0 && ( diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx new file mode 100644 index 000000000..b5fe8a3a0 --- /dev/null +++ b/projects/app/src/pages/chat/team.tsx @@ -0,0 +1,521 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import Head from 'next/head'; +import { getTeamChatInfo } from '@/web/core/chat/api'; +import { useRouter } from 'next/router'; +import { + Box, + Flex, + useDisclosure, + Drawer, + DrawerOverlay, + DrawerContent, + useTheme +} from '@chakra-ui/react'; +import Avatar from '@/components/Avatar'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useQuery } from '@tanstack/react-query'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import SideBar from '@/components/SideBar'; +import PageContainer from '@/components/PageContainer'; +import { getChatListById } from '@/web/core/chat/api'; +import ChatHistorySlider from './components/ChatHistorySlider'; +import ChatHeader from './components/ChatHeader'; +import { serviceSideProps } from '@/web/common/utils/i18n'; +import { useTranslation } from 'next-i18next'; +import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils'; +import { useChatStore } from '@/web/core/chat/storeChat'; +import { customAlphabet } from 'nanoid'; +import { useLoading } from '@/web/common/hooks/useLoading'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); +import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; +import { streamFetch } from '@/web/common/api/fetch'; +import { useTeamShareChatStore } from '@/web/core/chat/storeTeamChat'; +import type { + ChatHistoryItemType, + chatAppListSchema, + teamInfoType +} from '@fastgpt/global/core/chat/type.d'; +import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils'; +import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants'; +import { POST } from '@/web/common/api/request'; +const OutLink = ({ + teamId, + appId, + chatId, + authToken +}: { + teamId: string; + appId: string; + chatId: string; + authToken: string; +}) => { + const { t } = useTranslation(); + const router = useRouter(); + const { toast } = useToast(); + const theme = useTheme(); + const [myApps, setMyApps] = useState>([]); + const { isPc } = useSystemStore(); + const ChatBoxRef = useRef(null); + const [teamInfo, setTeamInfo] = useState(); + const { Loading, setIsLoading } = useLoading(); + const forbidRefresh = useRef(false); + + const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); + const { + histories, + loadHistories, + lastChatAppId, + setLastChatAppId, + lastChatId, + setLastChatId, + pushHistory, + updateHistory, + delOneHistory, + chatData, + setChatData, + delOneHistoryItem, + clearHistories + } = useChatStore(); + const { + localUId, + teamShareChatHistory, // abandon + clearLocalHistory // abandon + } = useTeamShareChatStore(); + + const outLinkUid: string = authToken || localUId; + + // 纯网络获取流程 + const loadApps = useCallback(async () => { + try { + if (!teamId) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + return; + } + // 根据teamId 获取教研token以及用户tags,然后通过是否为 + // 根据获取历史记录列表 + const res = await getChatListById({ teamId, authToken }); + const { apps = [], teamInfo } = res; + setMyApps(apps); + setTeamInfo(teamInfo); + if (apps.length <= 0) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + } + // + return null; + } catch (error: any) { + toast({ + status: 'warning', + title: error?.message + }); + } + }, [outLinkUid, router, t, toast]); + + const startChat = useCallback( + async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { + console.log('res', 13); + const prompts = messages.slice(-2); + const completionChatId = chatId ? chatId : nanoid(); + + const { responseText, responseData } = await streamFetch({ + data: { + messages: prompts, + variables, + appId, + teamId: teamId, + outLinkUid: outLinkUid, + chatId: completionChatId + }, + onMessage: generatingMessage, + abortCtrl: controller + }); + + const newTitle = + chatContentReplaceBlock(prompts[0].content).slice(0, 20) || + prompts[1]?.value?.slice(0, 20) || + t('core.chat.New Chat'); + + // new chat + if (completionChatId !== chatId) { + const newHistory: ChatHistoryItemType = { + chatId: completionChatId, + updateTime: new Date(), + title: newTitle, + appId, + top: false + }; + pushHistory(newHistory); + if (controller.signal.reason !== 'leave') { + forbidRefresh.current = true; + router.replace({ + query: { + chatId: completionChatId, + appId, + teamId: teamId, + authToken: authToken + } + }); + } + } else { + // update chat + const currentChat = histories.find((item) => item.chatId === chatId); + currentChat && + updateHistory({ + ...currentChat, + updateTime: new Date(), + title: newTitle + }); + } + // update chat window + setChatData((state) => ({ + ...state, + title: newTitle, + history: ChatBoxRef.current?.getChatHistories() || state.history + })); + + return { responseText, responseData, isNewChat: forbidRefresh.current }; + }, + [appId, chatId, histories, pushHistory, router, setChatData, updateHistory] + ); + + const { isFetching } = useQuery(['init', appId, teamId], async () => { + console.log('res', 3); + if (!teamId) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + return; + } + return teamId && loadApps(); + }); + + useQuery(['loadHistories', appId], () => { + console.log('res', 1); + teamId && appId ? loadHistories({ appId, outLinkUid }) : null; + }); + // 初始化聊天框 + useQuery(['init', { appId, chatId }], () => { + if (!teamId) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + return; + } + // pc: redirect to latest model chat + if (!appId && lastChatAppId) { + return router.replace({ + query: { + appId: lastChatAppId, + chatId: lastChatId, + teamId: teamId, + authToken: authToken + } + }); + } + if (!appId && myApps[0]) { + return router.replace({ + query: { + appId: myApps[0]._id, + chatId: lastChatId, + teamId: teamId, + authToken: authToken + } + }); + } + if (!appId) { + (async () => { + const { apps = [] } = await getChatListById({ teamId, authToken }); + setMyApps(apps); + if (apps.length === 0) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + } else { + router.replace({ + query: { + appId: apps[0]._id, + chatId: lastChatId, + teamId: teamId, + authToken: authToken + } + }); + } + })(); + return; + } + + // store id + appId && setLastChatAppId(appId); + setLastChatId(chatId); + return loadChatInfo({ + appId, + chatId, + loading: appId !== chatData.appId + }); + }); + + // get chat app info + const loadChatInfo = useCallback( + async ({ + appId, + chatId, + loading = false + }: { + appId: string; + chatId: string; + loading?: boolean; + }) => { + try { + if (!teamId) { + toast({ + status: 'error', + title: t('core.chat.You need to a chat app') + }); + return; + } + loading && setIsLoading(true); + const res = await getTeamChatInfo({ appId, chatId, outLinkUid }); + console.log('res', res); + const history = res.history.map((item) => ({ + ...item, + status: ChatStatusEnum.finish + })); + + setChatData({ + ...res, + history + }); + + // have records. + ChatBoxRef.current?.resetHistory(history); + ChatBoxRef.current?.resetVariables(res.variables); + if (res.history.length > 0) { + setTimeout(() => { + ChatBoxRef.current?.scrollToBottom('auto'); + }, 500); + } + } catch (e: any) { + // reset all chat tore + setLastChatAppId(''); + setLastChatId(''); + toast({ + title: t('core.chat.Failed to initialize chat'), + status: 'error' + }); + if (e?.code === 501) { + //router.replace('/app/list'); + } else if (chatId) { + router.replace({ + query: { + ...router.query, + chatId: '' + } + }); + } + } + setIsLoading(false); + return null; + }, + [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast] + ); + // 监测路由改变 + useEffect(() => { + const activeHistory = teamShareChatHistory.filter((item) => !item.delete); + if (!localUId || !teamId || activeHistory.length === 0) return; + (async () => { + try { + await POST('/core/chat/initLocalShareHistoryV464', { + outLinkUid: localUId, + chatIds: teamShareChatHistory.map((item) => item.chatId) + }); + clearLocalHistory(); + // router.reload(); + } catch (error) { + toast({ + status: 'warning', + title: t('core.shareChat.Init Error') + }); + } + })(); + }, [clearLocalHistory, localUId, router, teamShareChatHistory, teamId, t, toast]); + + return ( + + {/* pc show myself apps */} + + + + {myApps.map((item) => ( + { + router.replace({ + query: { + appId: item._id, + teamId: teamId, + authToken: authToken + } + }); + } + })} + > + + + {item.name} + + + ))} + + + + + + {((children: React.ReactNode) => { + return isPc || !appId ? ( + {children} + ) : ( + + + {children} + + ); + })( + ({ + id: item.chatId, + title: item.title, + customTitle: item.customTitle, + top: item.top + }))} + onChangeChat={(chatId) => { + router.replace({ + query: { + chatId: chatId || '', + appId, + teamId: teamId, + authToken: authToken + } + }); + if (!isPc) { + onCloseSlider(); + } + }} + onDelHistory={(e) => delOneHistory({ ...e, appId })} + onClearHistory={() => { + clearHistories({ appId }); + router.replace({ + query: { + appId, + teamId: teamId, + authToken: authToken + } + }); + }} + onSetHistoryTop={(e) => { + updateHistory({ ...e, appId }); + }} + onSetCustomTitle={async (e) => { + updateHistory({ + appId, + chatId: e.chatId, + title: e.title, + customTitle: e.title + }); + }} + /> + )} + {/* chat container */} + + {/* header */} + + {/* chat box */} + + {}} + onStartChat={startChat} + onDelMessage={(e) => + delOneHistoryItem({ ...e, appId: chatData.appId, chatId, outLinkUid }) + } + appId={chatData.appId} + chatId={chatId} + outLinkUid={outLinkUid} + /> + + + + + + ); +}; + +export async function getServerSideProps(context: any) { + const teamId = context?.query?.teamId || ''; + const appId = context?.query?.appId || ''; + const chatId = context?.query?.chatId || ''; + const authToken: string = context?.query?.authToken || ''; + + return { + props: { + teamId, + appId, + chatId, + authToken, + ...(await serviceSideProps(context)) + } + }; +} + +export default OutLink; diff --git a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx index 00a8d479d..fb6c5525c 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx @@ -1,6 +1,5 @@ import React, { useContext, useCallback, createContext, useState, useMemo, useEffect } from 'react'; -import { formatModelPrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants'; import { useTranslation } from 'next-i18next'; @@ -34,7 +33,7 @@ type useImportStoreType = { totalChunkChars: number; totalChunks: number; chunkSize: number; - predictPrice: number; + predictPoints: number; priceTip: string; uploadRate: number; splitSources2Chunks: () => void; @@ -54,7 +53,7 @@ const StateContext = createContext({ totalChunkChars: 0, totalChunks: 0, chunkSize: 0, - predictPrice: 0, + predictPoints: 0, priceTip: '', uploadRate: 50, splitSources2Chunks: () => {} @@ -105,10 +104,9 @@ const Provider = ({ chunkSize: embeddingChunkSize, showChunkInput: true, showPromptInput: false, - inputPrice: vectorModel.inputPrice, - outputPrice: 0, + charsPointsPrice: vectorModel.charsPointsPrice, priceTip: t('core.dataset.import.Embedding Estimated Price Tips', { - price: vectorModel.inputPrice + price: vectorModel.charsPointsPrice }), uploadRate: 150 }, @@ -120,10 +118,9 @@ const Provider = ({ chunkSize: agentModel.maxContext * 0.55 || 6000, showChunkInput: false, showPromptInput: true, - inputPrice: agentModel.inputPrice, - outputPrice: agentModel.outputPrice, + charsPointsPrice: agentModel.charsPointsPrice, priceTip: t('core.dataset.import.QA Estimated Price Tips', { - price: agentModel?.inputPrice + price: agentModel?.charsPointsPrice }), uploadRate: 30 } @@ -151,15 +148,12 @@ const Provider = ({ () => sources.reduce((sum, file) => sum + file.chunkChars, 0), [sources] ); - const predictPrice = useMemo(() => { + const predictPoints = useMemo(() => { if (mode === TrainingModeEnum.qa) { - const inputTotal = totalChunkChars * selectModelStaticParam.inputPrice; - const outputTotal = totalChunkChars * 0.5 * selectModelStaticParam.inputPrice; - - return formatModelPrice2Read(inputTotal + outputTotal); + return +(((totalChunkChars * 1.5) / 1000) * agentModel.charsPointsPrice).toFixed(2); } - return formatModelPrice2Read(totalChunkChars * selectModelStaticParam.inputPrice); - }, [mode, selectModelStaticParam.inputPrice, totalChunkChars]); + return +((totalChunkChars / 1000) * vectorModel.charsPointsPrice).toFixed(2); + }, [agentModel.charsPointsPrice, mode, totalChunkChars, vectorModel.charsPointsPrice]); const totalChunks = useMemo( () => sources.reduce((sum, file) => sum + file.chunks.length, 0), [sources] @@ -178,7 +172,8 @@ const Provider = ({ return { ...file, chunkChars: chars, - chunks: chunks.map((chunk) => ({ + chunks: chunks.map((chunk, i) => ({ + chunkIndex: i, q: chunk, a: '' })) @@ -198,7 +193,7 @@ const Provider = ({ totalChunkChars, totalChunks, chunkSize, - predictPrice, + predictPoints, splitSources2Chunks }; return {children}; diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx index 2f3eef5cf..d34e536a2 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx @@ -46,7 +46,7 @@ function DataProcess({ maxChunkSize, totalChunkChars, totalChunks, - predictPrice, + predictPoints, showRePreview, splitSources2Chunks, priceTip @@ -275,7 +275,7 @@ function DataProcess({ {feConfigs?.show_pay && ( - {t('core.dataset.import.Estimated Price', { amount: predictPrice, unit: '元' })} + {t('core.dataset.import.Estimated points', { points: predictPoints })} )} diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx index f7e0475d6..c3bd68733 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx @@ -16,7 +16,7 @@ import { useImportStore, type FormType } from '../Provider'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRequest } from '@/web/common/hooks/useRequest'; -import { postCreateTrainingBill } from '@/web/support/wallet/bill/api'; +import { postCreateTrainingUsage } from '@/web/support/wallet/usage/api'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { chunksUpload, fileCollectionCreate } from '@/web/core/dataset/utils'; import { ImportSourceItemType } from '@/web/core/dataset/type'; @@ -54,7 +54,7 @@ const Upload = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => { // Batch create collection and upload chunks for await (const item of uploadList) { - const billId = await postCreateTrainingBill({ + const billId = await postCreateTrainingUsage({ name: item.sourceName, datasetId: datasetDetail._id }); diff --git a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx index 14bfb5ddb..ab7e133e0 100644 --- a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx +++ b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx @@ -19,7 +19,6 @@ import { useRequest } from '@/web/common/hooks/useRequest'; import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { useConfirm } from '@/web/common/hooks/useConfirm'; import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils'; -import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type'; import SideTabs from '@/components/SideTabs'; import DeleteIcon from '@fastgpt/web/components/common/Icon/delete'; @@ -162,9 +161,10 @@ const InputDataModal = ({ q: e.q, a: e.a, // remove dataId - indexes: e.indexes.map((index) => - index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index - ) + indexes: e.indexes.map((index) => ({ + ...index, + dataId: undefined + })) }); return { @@ -195,7 +195,7 @@ const InputDataModal = ({ id: dataId, ...e, indexes: e.indexes.map((index) => - index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index + index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a, dataId: index.dataId }) : index ) }); @@ -278,7 +278,7 @@ const InputDataModal = ({ bg={i % 2 !== 0 ? 'myWhite.400' : ''} _hover={{ '& .delete': { - display: index.defaultIndex && indexes.length === 1 ? 'none' : 'block' + display: index.defaultIndex ? 'none' : 'block' } }} > @@ -331,7 +331,6 @@ const InputDataModal = ({ onClick={() => appendIndexes({ defaultIndex: false, - type: DatasetDataIndexTypeEnum.chunk, text: '', dataId: `${Date.now()}` }) @@ -383,45 +382,47 @@ const InputTab = ({ const [inputType, setInputType] = useState(InputTypeEnum.q); return ( - - - - * - - {t('core.dataset.data.Main Content')} - - - - - ), - value: InputTypeEnum.q - }, - { - label: ( - - {t('core.dataset.data.Auxiliary Data')} - - - - - ), - value: InputTypeEnum.a - } - ]} - value={inputType} - onChange={(e) => setInputType(e as InputTypeEnum)} - /> + + + + + * + + {t('core.dataset.data.Main Content')} + + + + + ), + value: InputTypeEnum.q + }, + { + label: ( + + {t('core.dataset.data.Auxiliary Data')} + + + + + ), + value: InputTypeEnum.a + } + ]} + value={inputType} + onChange={(e) => setInputType(e as InputTypeEnum)} + /> +
- + {inputType === InputTypeEnum.q && (