From 322ca757af0d59a9f8c5a47a0da9eb165e2bb2d9 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Thu, 29 Aug 2024 14:51:34 +0800 Subject: [PATCH] 4.8.10 test (#2568) * perf: i18n perf * fix: detail=fasle response * fix: dataset tag load repeat * feat :doc * perf: rename fun * code comment --- .../zh-cn/docs/development/upgrading/4810.md | 27 +++-- packages/global/core/workflow/utils.ts | 5 +- packages/service/common/file/read/utils.ts | 4 +- .../service/core/workflow/dispatch/index.ts | 4 +- .../core/workflow/dispatch/tools/readFiles.ts | 40 ++++--- .../workflow/dispatch/tools/runUpdateVar.ts | 4 +- packages/web/i18n/en/user.json | 4 + packages/web/i18n/zh/user.json | 4 + .../pages/account/components/Promotion.tsx | 4 +- projects/app/src/pages/api/admin/initv4810.ts | 4 +- .../app/src/pages/api/common/file/upload.ts | 6 +- .../app/src/pages/api/v1/chat/completions.ts | 8 ++ .../components/CollectionCard/Header.tsx | 5 +- .../CollectionCard/HeaderTagPopOver.tsx | 78 ++++++------- .../CollectionCard/TagManageModal.tsx | 50 +++------ .../components/CollectionCard/TagsPopOver.tsx | 97 ++++++---------- .../app/src/pages/dataset/detail/index.tsx | 4 - .../dataset/context/datasetPageContext.tsx | 104 ++++++++++++------ 18 files changed, 229 insertions(+), 223 deletions(-) diff --git a/docSite/content/zh-cn/docs/development/upgrading/4810.md b/docSite/content/zh-cn/docs/development/upgrading/4810.md index 34440338661a..36a41731dcda 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4810.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4810.md @@ -65,15 +65,18 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \ 16. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。 17. 优化 - 最新 React Markdown 组件,支持 Base64 图片。 18. 优化 - 知识库列表 UI。 -19. 优化 - 支持无网络配置情况下运行。 -20. 优化 - 部分全局变量,增加数据类型约束。 -21. 修复 - 全局变量 key 可能重复。 -22. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。 -23. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。 -24. 修复 - 选择 Milvus 部署时,无法导出知识库。 -25. 修复 - 创建 APP 副本,无法复制系统配置。 -26. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。 -27. 修复 - 内容提取的数据类型与输出数据类型未一致。 -28. 修复 - 工作流运行时间统计错误。 -29. 修复 - stream 模式下,工具调用有可能出现 undefined -30. 修复 - 全局变量在 API 中无法持久化。 +19. 优化 - 知识库详情页 UI。 +20. 优化 - 支持无网络配置情况下运行。 +21. 优化 - 部分全局变量,增加数据类型约束。 +22. 修复 - 全局变量 key 可能重复。 +23. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。 +24. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。 +25. 修复 - 选择 Milvus 部署时,无法导出知识库。 +26. 修复 - 创建 APP 副本,无法复制系统配置。 +27. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。 +28. 修复 - 内容提取的数据类型与输出数据类型未一致。 +29. 修复 - 工作流运行时间统计错误。 +30. 修复 - stream 模式下,工具调用有可能出现 undefined +31. 修复 - 全局变量在 API 中无法持久化。 +32. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题) +33. 修复 - 知识库标签重复加载。 diff --git a/packages/global/core/workflow/utils.ts b/packages/global/core/workflow/utils.ts index f826a0b30b6e..65cdd9d71d9a 100644 --- a/packages/global/core/workflow/utils.ts +++ b/packages/global/core/workflow/utils.ts @@ -333,7 +333,8 @@ export const removePluginInputVariables = ( ); }; -export function replaceVariableLabel({ +// replace {{$xx.xx$}} variables for text +export function replaceEditorVariable({ text, nodes, variables, @@ -341,7 +342,7 @@ export function replaceVariableLabel({ }: { text: any; nodes: RuntimeNodeItemType[]; - variables: Record; + variables: Record; // global variables runningNode: RuntimeNodeItemType; }) { if (typeof text !== 'string') return text; diff --git a/packages/service/common/file/read/utils.ts b/packages/service/common/file/read/utils.ts index bdcd5f2cedaf..8f14eb9a3773 100644 --- a/packages/service/common/file/read/utils.ts +++ b/packages/service/common/file/read/utils.ts @@ -79,7 +79,7 @@ export const readRawContentByFileBuffer = async ({ ) return; - addLog.info('Use custom read file service'); + const start = Date.now(); const data = new FormData(); data.append('file', buffer, { @@ -101,6 +101,8 @@ export const readRawContentByFileBuffer = async ({ } }); + addLog.info(`Use custom read file service, time: ${Date.now() - start}ms`); + const rawText = response.data.markdown; return { diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index a06737078ded..564e2d94c035 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -21,7 +21,7 @@ import { } from '@fastgpt/global/core/workflow/node/constant'; import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { getSystemTime } from '@fastgpt/global/common/time/timezone'; -import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils'; +import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils'; import { dispatchWorkflowStart } from './init/workflowStart'; import { dispatchChatCompletion } from './chat/oneapi'; @@ -368,7 +368,7 @@ export async function dispatchWorkFlow(data: Props): Promise => { // Concat fileUrlList and filesFromHistories; remove not supported files const parseUrlList = [...fileUrlList, ...filesFromHistories] .map((url) => { - // System file - if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) { - // Parse url, get filename query. Keep only documents that can be parsed - const parseUrl = new URL(url); - const filenameQuery = parseUrl.searchParams.get('filename'); - if (filenameQuery) { - const extensionQuery = filenameQuery.split('.').pop()?.toLowerCase() || ''; - if (!documentFileType.includes(extensionQuery)) { - return ''; + try { + // Avoid "/api/xxx" file error. + const origin = requestOrigin ?? 'http://localhost:3000'; + + // Check is system upload file + if (url.startsWith('/') || (requestOrigin && url.startsWith(requestOrigin))) { + // Parse url, get filename query. Keep only documents that can be parsed + const parseUrl = new URL(url, origin); + const filenameQuery = parseUrl.searchParams.get('filename'); + + // Not document + if (filenameQuery) { + const extensionQuery = filenameQuery.split('.').pop()?.toLowerCase() || ''; + if (!documentFileType.includes(extensionQuery)) { + return ''; + } } - } - // Remove the origin(Make intranet requests directly) - if (requestOrigin && url.startsWith(requestOrigin)) { - url = url.replace(requestOrigin, ''); + // Remove the origin(Make intranet requests directly) + if (requestOrigin && url.startsWith(requestOrigin)) { + url = url.replace(requestOrigin, ''); + } } - } - return url; + return url; + } catch (error) { + console.log(error); + return ''; + } }) .filter(Boolean) .slice(0, maxFiles); diff --git a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts index 9a95c6ea0db6..bee728281efe 100644 --- a/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts +++ b/packages/service/core/workflow/dispatch/tools/runUpdateVar.ts @@ -8,7 +8,7 @@ import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type'; import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import { removeSystemVariable, valueTypeFormat } from '../utils'; -import { replaceVariableLabel } from '@fastgpt/global/core/workflow/utils'; +import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils'; type Props = ModuleDispatchProps<{ [NodeInputKeyEnum.updateList]: TUpdateListItem[]; @@ -32,7 +32,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise => const formatValue = valueTypeFormat(item.value?.[1], item.valueType); return typeof formatValue === 'string' - ? replaceVariableLabel({ + ? replaceEditorVariable({ text: formatValue, nodes: runtimeNodes, variables, diff --git a/packages/web/i18n/en/user.json b/packages/web/i18n/en/user.json index d530837d8e3e..ae47a77dac7a 100644 --- a/packages/web/i18n/en/user.json +++ b/packages/web/i18n/en/user.json @@ -20,6 +20,10 @@ "you_can_convert": "You can redeem", "yuan": "Yuan" }, + "promotion": { + "register": "Register", + "pay": "Pay" + }, "bind_inform_account_error": "Abnormal binding notification account", "bind_inform_account_success": "Binding notification account successful", "code_error": { diff --git a/packages/web/i18n/zh/user.json b/packages/web/i18n/zh/user.json index 912dca343be9..feefae436135 100644 --- a/packages/web/i18n/zh/user.json +++ b/packages/web/i18n/zh/user.json @@ -20,6 +20,10 @@ "current_token_price": "当前积分价格", "yuan": "元" }, + "promotion": { + "register": "好友注册", + "pay": "好友充值" + }, "bind_inform_account_error": "绑定通知账号异常", "bind_inform_account_success": "绑定通知账号成功", "delete": { diff --git a/projects/app/src/pages/account/components/Promotion.tsx b/projects/app/src/pages/account/components/Promotion.tsx index 87dca1d3e1bd..7543f4566bdc 100644 --- a/projects/app/src/pages/account/components/Promotion.tsx +++ b/projects/app/src/pages/account/components/Promotion.tsx @@ -19,10 +19,8 @@ import { useQuery } from '@tanstack/react-query'; import { getPromotionInitData, getPromotionRecords } from '@/web/support/activity/promotion/api'; import { useUserStore } from '@/web/support/user/useUserStore'; -import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { useCopyData } from '@/web/common/hooks/useCopyData'; import type { PromotionRecordType } from '@/global/support/api/userRes.d'; -import MyIcon from '@fastgpt/web/components/common/Icon'; import dayjs from 'dayjs'; import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { useLoading } from '@fastgpt/web/hooks/useLoading'; @@ -116,7 +114,7 @@ const Promotion = () => { {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} - {t(`user.promotion.${item.type}` as any)} + {t(`user:promotion.${item.type}` as any)} {item.amount} ))} diff --git a/projects/app/src/pages/api/admin/initv4810.ts b/projects/app/src/pages/api/admin/initv4810.ts index 21ea10c86aa0..8df8f15c5811 100644 --- a/projects/app/src/pages/api/admin/initv4810.ts +++ b/projects/app/src/pages/api/admin/initv4810.ts @@ -2,13 +2,11 @@ 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 { MongoDataset } from '@fastgpt/service/core/dataset/schema'; -import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant'; import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { FastGPTProUrl } from '@fastgpt/service/common/system/constants'; import { POST } from '@fastgpt/service/common/api/plusRequest'; -/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ +/* 初始化发布的版本 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); diff --git a/projects/app/src/pages/api/common/file/upload.ts b/projects/app/src/pages/api/common/file/upload.ts index aebb830952f3..11c00bba0523 100644 --- a/projects/app/src/pages/api/common/file/upload.ts +++ b/projects/app/src/pages/api/common/file/upload.ts @@ -30,7 +30,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { }); const { file, bucketName, metadata } = await upload.doUpload(req, res); filePaths.push(file.path); - const { teamId, tmbId, outLinkUid } = await authChatCert({ req, authToken: true }); + const { teamId, tmbId, outLinkUid } = await authChatCert({ + req, + authToken: true, + authApiKey: true + }); await authUploadLimit(outLinkUid || tmbId); diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 3c1a9d410c0c..434b75a6fa5a 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -368,6 +368,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { if (assistantResponses.length === 0) return ''; if (assistantResponses.length === 1 && assistantResponses[0].text?.content) return assistantResponses[0].text?.content; + + if (!detail) { + return assistantResponses + .map((item) => item?.text?.content) + .filter(Boolean) + .join('\n'); + } + return assistantResponses; })(); diff --git a/projects/app/src/pages/dataset/detail/components/CollectionCard/Header.tsx b/projects/app/src/pages/dataset/detail/components/CollectionCard/Header.tsx index d85f610bd7e7..30b9ac82d62d 100644 --- a/projects/app/src/pages/dataset/detail/components/CollectionCard/Header.tsx +++ b/projects/app/src/pages/dataset/detail/components/CollectionCard/Header.tsx @@ -125,7 +125,7 @@ const Header = ({}: {}) => { return ( - + ({ parentId: path.parentId, @@ -173,7 +173,8 @@ const Header = ({}: {}) => { {/* search input */} {isPc && ( { const { t } = useTranslation(); - const [searchTag, setSearchTag] = useState(''); const [checkedTags, setCheckedTags] = useState([]); - const { datasetDetail, datasetTags, loadDatasetTags, checkedDatasetTag, setCheckedDatasetTag } = - useContextSelector(DatasetPageContext, (v) => v); - - const { mutate: onCreateCollectionTag, isLoading: isCreateCollectionTagLoading } = useRequest({ - mutationFn: async (tag: string) => { - const id = await postCreateDatasetCollectionTag({ - datasetId: datasetDetail._id, - tag - }); - return id; - }, - - onSuccess() { - setSearchTag(''); - }, - successToast: t('common:common.Create Success'), - errorToast: t('common:common.Create Failed') - }); + const { + searchDatasetTagsResult, + searchTagKey, + setSearchTagKey, + checkedDatasetTag, + setCheckedDatasetTag, + onCreateCollectionTag, + isCreateCollectionTagLoading + } = useContextSelector(DatasetPageContext, (v) => v); const { filterTags, setFilterTags, getData } = useContextSelector( CollectionPageContext, @@ -48,10 +36,6 @@ const HeaderTagPopOver = () => { [] ); - useEffect(() => { - loadDatasetTags({ id: datasetDetail._id, searchKey: searchTag }); - }, [searchTag]); - const { isOpen: isTagManageModalOpen, onOpen: onOpenTagManageModal, @@ -122,35 +106,34 @@ const HeaderTagPopOver = () => { pl={2} h={8} borderRadius={'xs'} - value={searchTag} + value={searchTagKey} placeholder={t('dataset:tag.searchOrAddTag')} - onChange={(e) => setSearchTag(e.target.value)} + onChange={(e) => setSearchTagKey(e.target.value)} /> - {searchTag && !datasetTags.map((item) => item.tag).includes(searchTag) && ( - { - onCreateCollectionTag(searchTag); - }} - > - - - {t('dataset:tag.add') + ` "${searchTag}"`} - - - )} + {searchTagKey && + !searchDatasetTagsResult.map((item) => item.tag).includes(searchTagKey) && ( + onCreateCollectionTag(searchTagKey)} + > + + + {t('dataset:tag.add') + ` "${searchTagKey}"`} + + + )} {[ ...new Map( - [...checkedDatasetTag, ...datasetTags].map((item) => [item._id, item]) + [...checkedDatasetTag, ...searchDatasetTagsResult].map((item) => [item._id, item]) ).values() ].map((item) => { const checked = checkedTags.includes(item._id); @@ -197,6 +180,7 @@ const HeaderTagPopOver = () => { borderBottomLeftRadius={'md'} variant={'unstyled'} onClick={() => { + setSearchTagKey(''); setCheckedTags([]); setFilterTags([]); debounceRefetch(); diff --git a/projects/app/src/pages/dataset/detail/components/CollectionCard/TagManageModal.tsx b/projects/app/src/pages/dataset/detail/components/CollectionCard/TagManageModal.tsx index 33a72a64f488..c6aa7b6314e1 100644 --- a/projects/app/src/pages/dataset/detail/components/CollectionCard/TagManageModal.tsx +++ b/projects/app/src/pages/dataset/detail/components/CollectionCard/TagManageModal.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Input, Button, Flex, Box, Checkbox, BoxProps } from '@chakra-ui/react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { Input, Button, Flex, Box, Checkbox } from '@chakra-ui/react'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; @@ -13,10 +13,9 @@ import { getScrollCollectionList, getTagUsage, postAddTagsToCollections, - postCreateDatasetCollectionTag, updateDatasetCollectionTag } from '@/web/core/dataset/api'; -import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import MyInput from '@/components/MyInput'; import { DatasetTagType } from '@fastgpt/global/core/dataset/type'; import { ScrollListType, useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; @@ -26,9 +25,13 @@ import { DatasetCollectionsListItemType } from '@/global/core/dataset/type'; const TagManageModal = ({ onClose }: { onClose: () => void }) => { const { t } = useTranslation(); - const datasetDetail = useContextSelector(DatasetPageContext, (v) => v.datasetDetail); - const loadDatasetTags = useContextSelector(DatasetPageContext, (v) => v.loadDatasetTags); - const loadAllDatasetTags = useContextSelector(DatasetPageContext, (v) => v.loadAllDatasetTags); + const { + datasetDetail, + onCreateCollectionTag, + isCreateCollectionTagLoading, + loadAllDatasetTags, + setSearchTagKey + } = useContextSelector(DatasetPageContext, (v) => v); const { getData, pageNum, collections } = useContextSelector(CollectionPageContext, (v) => v); const tagInputRef = useRef(null); @@ -56,36 +59,17 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => { } }, [currentEditTag]); - const { mutate: onCreateCollectionTag, isLoading: isCreateCollectionTagLoading } = useRequest({ - mutationFn: async (tag: string) => { - const id = await postCreateDatasetCollectionTag({ - datasetId: datasetDetail._id, - tag - }); - return id; - }, - - onSuccess() { - fetchData(1); - loadDatasetTags({ id: datasetDetail._id, searchKey: '' }); - loadAllDatasetTags({ id: datasetDetail._id }); - }, - successToast: t('common:common.Create Success'), - errorToast: t('common:common.Create Failed') - }); - const { runAsync: onDeleteCollectionTag, loading: isDeleteCollectionTagLoading } = useRequest2( - (tag: string) => { - return delDatasetCollectionTag({ + (tag: string) => + delDatasetCollectionTag({ datasetId: datasetDetail._id, id: tag - }); - }, + }), { onSuccess() { fetchData(1); - loadDatasetTags({ id: datasetDetail._id, searchKey: '' }); - loadAllDatasetTags({ id: datasetDetail._id }); + setSearchTagKey(''); + loadAllDatasetTags(); }, successToast: t('common:common.Delete Success'), errorToast: t('common:common.Delete Failed') @@ -103,8 +87,8 @@ const TagManageModal = ({ onClose }: { onClose: () => void }) => { { onSuccess() { fetchData(1); - loadDatasetTags({ id: datasetDetail._id, searchKey: '' }); - loadAllDatasetTags({ id: datasetDetail._id }); + setSearchTagKey(''); + loadAllDatasetTags(); } } ); diff --git a/projects/app/src/pages/dataset/detail/components/CollectionCard/TagsPopOver.tsx b/projects/app/src/pages/dataset/detail/components/CollectionCard/TagsPopOver.tsx index 5fc93ebabf07..1d033c4d326d 100644 --- a/projects/app/src/pages/dataset/detail/components/CollectionCard/TagsPopOver.tsx +++ b/projects/app/src/pages/dataset/detail/components/CollectionCard/TagsPopOver.tsx @@ -2,12 +2,11 @@ import { Box, Checkbox, Flex, Input } from '@chakra-ui/react'; import MyPopover from '@fastgpt/web/components/common/MyPopover'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import { postCreateDatasetCollectionTag, putDatasetCollectionById } from '@/web/core/dataset/api'; +import { putDatasetCollectionById } from '@/web/core/dataset/api'; import { useContextSelector } from 'use-context-selector'; import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext'; import { useTranslation } from 'next-i18next'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import { useRequest } from '@fastgpt/web/hooks/useRequest'; +import { useMemo, useRef, useState } from 'react'; import { useDeepCompareEffect } from 'ahooks'; import { DatasetCollectionItemType, DatasetTagType } from '@fastgpt/global/core/dataset/type'; import { isEqual } from 'lodash'; @@ -19,25 +18,20 @@ const TagsPopOver = ({ currentCollection: DatasetCollectionItemType | DatasetCollectionsListItemType; }) => { const { t } = useTranslation(); - const datasetDetail = useContextSelector(DatasetPageContext, (v) => v.datasetDetail); - const datasetTags = useContextSelector(DatasetPageContext, (v) => v.datasetTags); - const loadDatasetTags = useContextSelector(DatasetPageContext, (v) => v.loadDatasetTags); - const allDatasetTags = useContextSelector(DatasetPageContext, (v) => v.allDatasetTags); - const loadAllDatasetTags = useContextSelector(DatasetPageContext, (v) => v.loadAllDatasetTags); + const { + searchTagKey, + setSearchTagKey, + searchDatasetTagsResult, + allDatasetTags, + onCreateCollectionTag, + isCreateCollectionTagLoading + } = useContextSelector(DatasetPageContext, (v) => v); - const [collectionTags, setCollectionTags] = useState([]); - const [searchTag, setSearchTag] = useState(''); + const [collectionTags, setCollectionTags] = useState(currentCollection.tags ?? []); const [checkedTags, setCheckedTags] = useState([]); - const [showTagManage, setShowTagManage] = useState(false); - const [isFocusInput, setIsFocusInput] = useState(false); const [isUpdateLoading, setIsUpdateLoading] = useState(false); - useEffect(() => { - if (!currentCollection.tags) return; - setCollectionTags(currentCollection.tags); - }, [currentCollection]); - const tagList = useMemo( () => (collectionTags @@ -52,12 +46,6 @@ const TagsPopOver = ({ [collectionTags, allDatasetTags] ); - useEffect(() => { - if (!isFocusInput) return; - loadDatasetTags({ id: datasetDetail._id, searchKey: searchTag }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [datasetDetail._id, isFocusInput, searchTag]); - const [visibleTags, setVisibleTags] = useState(tagList); const [overflowTags, setOverflowTags] = useState([]); const containerRef = useRef(null); @@ -96,24 +84,6 @@ const TagsPopOver = ({ }; }, [tagList]); - const { mutate: onCreateCollectionTag, isLoading: isCreateCollectionTagLoading } = useRequest({ - mutationFn: async (tag: string) => { - const id = await postCreateDatasetCollectionTag({ - datasetId: datasetDetail._id, - tag - }); - return id; - }, - - onSuccess() { - setSearchTag(''); - loadDatasetTags({ id: datasetDetail._id, searchKey: '' }); - loadAllDatasetTags({ id: datasetDetail._id }); - }, - successToast: t('common:common.Create Success'), - errorToast: t('common:common.Create Failed') - }); - return ( } onCloseFunc={async () => { + setSearchTagKey(''); + setShowTagManage(false); if (isEqual(checkedTags, tagList) || !showTagManage) return; setIsUpdateLoading(true); @@ -194,36 +166,33 @@ const TagsPopOver = ({ e.stopPropagation()}> setIsFocusInput(true)} - onBlur={() => setIsFocusInput(false)} pl={2} h={7} borderRadius={'xs'} - value={searchTag} + value={searchTagKey} placeholder={t('dataset:tag.searchOrAddTag')} - onChange={(e) => setSearchTag(e.target.value)} + onChange={(e) => setSearchTagKey(e.target.value)} /> - {searchTag && !datasetTags.map((item) => item.tag).includes(searchTag) && ( - { - onCreateCollectionTag(searchTag); - }} - > - - - {t('dataset:tag.add') + ` "${searchTag}"`} - - - )} - {datasetTags?.map((item) => { + {searchTagKey && + !searchDatasetTagsResult.map((item) => item.tag).includes(searchTagKey) && ( + onCreateCollectionTag(searchTagKey)} + > + + + {t('dataset:tag.add') + ` "${searchTagKey}"`} + + + )} + {searchDatasetTagsResult?.map((item) => { const tagsList = checkedTags.map((tag) => tag.tag); return ( { const { isPc } = useSystem(); const datasetDetail = useContextSelector(DatasetPageContext, (v) => v.datasetDetail); const loadDatasetDetail = useContextSelector(DatasetPageContext, (v) => v.loadDatasetDetail); - const loadAllDatasetTags = useContextSelector(DatasetPageContext, (v) => v.loadAllDatasetTags); useRequest2(() => loadDatasetDetail(datasetId), { - onSuccess: () => { - loadAllDatasetTags({ id: datasetId }); - }, onError(err: any) { router.replace(`/dataset/list`); toast({ diff --git a/projects/app/src/web/core/dataset/context/datasetPageContext.tsx b/projects/app/src/web/core/dataset/context/datasetPageContext.tsx index 436e1364e04a..0c9a59092827 100644 --- a/projects/app/src/web/core/dataset/context/datasetPageContext.tsx +++ b/projects/app/src/web/core/dataset/context/datasetPageContext.tsx @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { ReactNode, SetStateAction, useMemo, useState } from 'react'; +import { Dispatch, ReactNode, SetStateAction, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'next-i18next'; import { createContext } from 'use-context-selector'; import { @@ -8,24 +8,30 @@ import { getDatasetCollectionTags, getDatasetTrainingQueue, getTrainingQueueLen, + postCreateDatasetCollectionTag, putDatasetById } from '../api'; import { defaultDatasetDetail } from '../constants'; import { DatasetUpdateBody } from '@fastgpt/global/core/dataset/api'; import { DatasetItemType, DatasetTagType } from '@fastgpt/global/core/dataset/type'; import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; type DatasetPageContextType = { datasetId: string; datasetDetail: DatasetItemType; loadDatasetDetail: (id: string) => Promise; updateDataset: (data: DatasetUpdateBody) => Promise; - datasetTags: DatasetTagType[]; - loadDatasetTags: (data: { id: string; searchKey: string }) => Promise; + + searchDatasetTagsResult: DatasetTagType[]; allDatasetTags: DatasetTagType[]; - loadAllDatasetTags: (data: { id: string }) => Promise; + loadAllDatasetTags: () => Promise; checkedDatasetTag: DatasetTagType[]; setCheckedDatasetTag: React.Dispatch>; + onCreateCollectionTag: (tag: string) => Promise; + isCreateCollectionTagLoading: boolean; + searchTagKey: string; + setSearchTagKey: Dispatch>; vectorTrainingMap: { colorSchema: string; @@ -62,16 +68,21 @@ export const DatasetPageContext = createContext({ updateDataset: function (data: DatasetUpdateBody): Promise { throw new Error('Function not implemented.'); }, - datasetTags: [], - loadDatasetTags: function (data: { id: string; searchKey: string }): Promise { + searchDatasetTagsResult: [], + allDatasetTags: [], + checkedDatasetTag: [], + setCheckedDatasetTag: function (): void { throw new Error('Function not implemented.'); }, - allDatasetTags: [], - loadAllDatasetTags: function (data: { id: string }): Promise { + loadAllDatasetTags: function (): Promise { throw new Error('Function not implemented.'); }, - checkedDatasetTag: [], - setCheckedDatasetTag: function (): void { + onCreateCollectionTag: function (tag: string): Promise { + throw new Error('Function not implemented.'); + }, + isCreateCollectionTagLoading: false, + searchTagKey: '', + setSearchTagKey: function (value: SetStateAction): void { throw new Error('Function not implemented.'); } }); @@ -108,28 +119,53 @@ export const DatasetPageContextProvider = ({ }; // dataset tags - const [datasetTags, setDatasetTags] = useState([]); - - const loadDatasetTags = async ({ id, searchKey }: { id: string; searchKey: string }) => { - const { list } = await getDatasetCollectionTags({ - datasetId: id, - searchText: searchKey, - current: 1, - pageSize: 15 - }); - setDatasetTags(list); - }; - const [checkedDatasetTag, setCheckedDatasetTag] = useState([]); + const [searchTagKey, setSearchTagKey] = useState(''); - const [allDatasetTags, setAllDatasetTags] = useState([]); - - const loadAllDatasetTags = async ({ id }: { id: string }) => { - if (!feConfigs?.isPlus) return; + const { runAsync: loadAllDatasetTags, data: allDatasetTags = [] } = useRequest2( + async () => { + if (!feConfigs?.isPlus || !datasetDetail._id) return []; - const { list } = await getAllTags(id); - setAllDatasetTags(list); - }; + const { list } = await getAllTags(datasetDetail._id); + return list; + }, + { + manual: false, + refreshDeps: [datasetDetail._id] + } + ); + const { data: searchDatasetTagsResult = [] } = useRequest2( + async () => { + if (!searchTagKey) return allDatasetTags; + const { list } = await getDatasetCollectionTags({ + datasetId: datasetDetail._id, + searchText: searchTagKey, + current: 1, + pageSize: 15 + }); + return list; + }, + { + manual: false, + throttleWait: 300, + refreshDeps: [datasetDetail._id, searchTagKey, allDatasetTags] + } + ); + const { runAsync: onCreateCollectionTag, loading: isCreateCollectionTagLoading } = useRequest2( + (tag: string) => + postCreateDatasetCollectionTag({ + datasetId: datasetDetail._id, + tag + }), + { + refreshDeps: [datasetDetail._id], + onSuccess() { + loadAllDatasetTags(); + }, + successToast: t('common:common.Create Success'), + errorToast: t('common:common.Create Failed') + } + ); // global queue const { data: { vectorTrainingCount = 0, agentTrainingCount = 0 } = {} } = useQuery( @@ -199,12 +235,16 @@ export const DatasetPageContextProvider = ({ rebuildingCount, trainingCount, refetchDatasetTraining, - datasetTags, - loadDatasetTags, + + searchDatasetTagsResult, checkedDatasetTag, setCheckedDatasetTag, allDatasetTags, - loadAllDatasetTags + loadAllDatasetTags, + onCreateCollectionTag, + isCreateCollectionTagLoading, + searchTagKey, + setSearchTagKey }; return {children};