From 7b82e1dcf880c0b66634f87a8e710485eb5ae4e2 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Mon, 17 Nov 2025 21:02:39 +0800 Subject: [PATCH] Update doc (#5934) * fix: text split * remove test * doc * doc * feat: support quick create dataset in app (#5940) * feat: support quick create dataset in app * doc * perf: create dataset modal * remove log --------- Co-authored-by: heheer --- README.md | 2 +- document/content/docs/upgrading/4-14/4142.mdx | 14 + document/data/doc-last-modified.json | 2 +- packages/global/core/app/type.d.ts | 2 +- packages/global/core/dataset/type.d.ts | 3 +- packages/global/core/workflow/type/io.d.ts | 2 +- .../core/workflow/dispatch/dataset/search.ts | 2 +- .../web/components/common/MyModal/index.tsx | 2 +- packages/web/i18n/en/app.json | 5 + packages/web/i18n/en/common.json | 1 - packages/web/i18n/zh-CN/app.json | 5 + packages/web/i18n/zh-CN/common.json | 1 - packages/web/i18n/zh-Hant/app.json | 5 + packages/web/i18n/zh-Hant/common.json | 1 - .../core/app/DatasetSelectModal.tsx | 593 ++++++++++-------- .../components/core/dataset/SelectModal.tsx | 6 +- .../RenderInput/templates/SelectDataset.tsx | 4 +- .../detail/components/QuickCreateModal.tsx | 347 ++++++++++ .../detail/Import/commonProgress/Upload.tsx | 1 - .../core/dataset/queues/datasetParse.ts | 2 +- 20 files changed, 735 insertions(+), 265 deletions(-) create mode 100644 projects/app/src/pageComponents/app/detail/components/QuickCreateModal.tsx diff --git a/README.md b/README.md index b53597e2c..1af322b1d 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b - [x] 知识库单点搜索测试 - [x] 对话时反馈引用并可修改与删除 - [x] 完整调用链路日志 - - [ ] 应用评测 + - [x] 应用评测 - [ ] 高级编排 DeBug 调试模式 - [ ] 应用节点日志 diff --git a/document/content/docs/upgrading/4-14/4142.mdx b/document/content/docs/upgrading/4-14/4142.mdx index 9910f251e..8fef2e21d 100644 --- a/document/content/docs/upgrading/4-14/4142.mdx +++ b/document/content/docs/upgrading/4-14/4142.mdx @@ -9,6 +9,7 @@ description: 'FastGPT V4.14.2 更新说明' 1. 封装底层 Agent Call 方式,支持工具连续调用时上下文的压缩,以及单个工具长响应的压缩。 2. 模板市场新 UI。 +3. 支持 Agent 编辑页快速创建知识库。 ## ⚙️ 优化 @@ -19,3 +20,16 @@ description: 'FastGPT V4.14.2 更新说明' 1. 简易应用模板未正常转化。 2. 工具调用中,包含两个以上连续用户选择时候,第二个用户选择异常。 +3. 门户中,团队应用类型错误。 + +## 插件 + +1. 修复:子工具头像丢失。 +2. 修复:模型头像丢失。 +3. 修复:Worker 中错误引用 mongoose 依赖,导致超过 10s 的工具运行报错。 +4. 优化:开发环境热更新时,不重复上传静态文件。 +5. 新增:5118 SEO 关键词挖掘工具。 +6. 新增:Tavity 内容提取高级配置。网页站点地图工具。 +7. 新增:微信公众号工具集。 +8. 新增:文档对比工具。 +9. 新增:kimiV2 和 GPT5.1 模型预设。 \ No newline at end of file diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 66d2ef55c..3e756af53 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -116,7 +116,7 @@ "document/content/docs/upgrading/4-13/4132.mdx": "2025-10-21T11:46:53+08:00", "document/content/docs/upgrading/4-14/4140.mdx": "2025-11-06T15:43:00+08:00", "document/content/docs/upgrading/4-14/4141.mdx": "2025-11-12T12:19:02+08:00", - "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-14T13:21:17+08:00", + "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-17T19:34:52+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index c099c3c53..7f7b897a1 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -112,7 +112,7 @@ export type AppSimpleEditFormType = { [NodeInputKeyEnum.aiChatJsonSchema]?: string; }; dataset: { - datasets: SelectedDatasetType; + datasets: SelectedDatasetType[]; } & AppDatasetSearchParamsType; selectedTools: FlowNodeTemplateType[]; chatConfig: AppChatConfigType; diff --git a/packages/global/core/dataset/type.d.ts b/packages/global/core/dataset/type.d.ts index 26df8a530..d854bd274 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -10,7 +10,8 @@ import type { SearchScoreTypeEnum, TrainingModeEnum, ChunkSettingModeEnum, - ChunkTriggerConfigTypeEnum + ChunkTriggerConfigTypeEnum, + ParagraphChunkAIModeEnum } from './constants'; import type { DatasetPermission } from '../../support/permission/dataset/controller'; import type { diff --git a/packages/global/core/workflow/type/io.d.ts b/packages/global/core/workflow/type/io.d.ts index d2fdbdf0e..1957cb627 100644 --- a/packages/global/core/workflow/type/io.d.ts +++ b/packages/global/core/workflow/type/io.d.ts @@ -128,7 +128,7 @@ export type SelectedDatasetType = { avatar: string; name: string; vectorModel: EmbeddingModelItemType; -}[]; +}; /* http node */ export type HttpParamAndHeaderItemType = { diff --git a/packages/service/core/workflow/dispatch/dataset/search.ts b/packages/service/core/workflow/dispatch/dataset/search.ts index 384efceb6..749ce620b 100644 --- a/packages/service/core/workflow/dispatch/dataset/search.ts +++ b/packages/service/core/workflow/dispatch/dataset/search.ts @@ -19,7 +19,7 @@ import { getDatasetSearchToolResponsePrompt } from '../../../../../global/core/a import { getNodeErrResponse } from '../utils'; type DatasetSearchProps = ModuleDispatchProps<{ - [NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType; + [NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType[]; [NodeInputKeyEnum.datasetSimilarity]: number; [NodeInputKeyEnum.datasetMaxTokens]: number; [NodeInputKeyEnum.userChatInput]?: string; diff --git a/packages/web/components/common/MyModal/index.tsx b/packages/web/components/common/MyModal/index.tsx index 3f1a33ab0..59f289497 100644 --- a/packages/web/components/common/MyModal/index.tsx +++ b/packages/web/components/common/MyModal/index.tsx @@ -95,7 +95,7 @@ const MyModal = ({ /> )} - + {title} diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 6f8c09397..7b59368a1 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -2,6 +2,7 @@ "Add_tool": "Add tool", "AutoOptimize": "Automatic optimization", "Click_to_delete_this_field": "Click to delete this field", + "Create_dataset": "Create a knowledge base", "Custom_params": "input parameters", "Edit_tool": "Edit tool", "Filed_is_deprecated": "This field is deprecated", @@ -126,6 +127,10 @@ "custom_plugin_user_guide_label": "User Guide", "custom_plugin_user_guide_placeholder": "Use markdown syntax", "dataset": "dataset", + "dataset.Select_dataset_model_tip": "Only knowledge bases with the same index model can be selected", + "dataset.create_dataset_tips": "For more advanced operations, please go to", + "dataset_create_success": "The knowledge base was created successfully and files are being indexed in the background.", + "dataset_empty_tips": "You don’t have a knowledge base yet, create one first.", "dataset_search_tool_description": "Call the \"Semantic Search\" and \"Full-text Search\" capabilities to find reference content that may be related to the problem from the \"Knowledge Base\". \nPrioritize calling this tool to assist in answering user questions.", "dataset_select": "Optional knowledge base", "day": "Day", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 6061b08a3..56c25c801 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -783,7 +783,6 @@ "dataset.Manual collection Tip": "Manual datasets allow you to create an empty container to hold data", "dataset.Move Failed": "Move Error", "dataset.Select Dataset": "Select This Dataset", - "dataset.Select Dataset Tips": "Only Datasets with the same index model can be selected", "dataset.Select Folder": "Enter Folder", "dataset.Training Name": "Data Training", "dataset.collections.Collection Embedding": "{{total}} Indexes", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index f8f34e8f2..65b90e90e 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -2,6 +2,7 @@ "Add_tool": "添加工具", "AutoOptimize": "自动优化", "Click_to_delete_this_field": "点击删除该字段", + "Create_dataset": "创建知识库", "Custom_params": "输入参数", "Edit_tool": "编辑工具", "Filed_is_deprecated": "该字段已弃用", @@ -129,6 +130,10 @@ "custom_plugin_user_guide_label": "使用说明", "custom_plugin_user_guide_placeholder": "使用 markdown 语法", "dataset": "知识库", + "dataset.Select_dataset_model_tip": "仅能选择同一个索引模型的知识库", + "dataset.create_dataset_tips": "更多高级操作请前往", + "dataset_create_success": "知识库创建成功,正在后台索引文件", + "dataset_empty_tips": "你还没有知识库,先创建一个吧", "dataset_search_tool_description": "调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容。优先调用该工具来辅助回答用户的问题。", "dataset_select": "可选知识库", "day": "日", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index bfed3a4a3..28b2b9e93 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -786,7 +786,6 @@ "dataset.Manual collection Tip": "手动数据集允许创建一个空的容器装入数据", "dataset.Move Failed": "移动出现错误~", "dataset.Select Dataset": "选择该知识库", - "dataset.Select Dataset Tips": "仅能选择同一个索引模型的知识库", "dataset.Select Folder": "进入文件夹", "dataset.Training Name": "数据训练", "dataset.collections.Collection Embedding": "{{total}} 组索引中", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index 43614ca6d..fb3666358 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -2,6 +2,7 @@ "Add_tool": "添加工具", "AutoOptimize": "自動優化", "Click_to_delete_this_field": "點擊刪除該字段", + "Create_dataset": "創建知識庫", "Custom_params": "輸入參數", "Filed_is_deprecated": "該字段已棄用", "HTTPTools_Create_Type": "創建方式", @@ -125,6 +126,10 @@ "custom_plugin_user_guide_label": "使用說明", "custom_plugin_user_guide_placeholder": "使用 markdown 語法", "dataset": "知識庫", + "dataset.Select_dataset_model_tip": "僅能選擇同一個索引模型的知識庫", + "dataset.create_dataset_tips": "更多高級操作請前往", + "dataset_create_success": "知識庫創建成功,正在後台索引文件", + "dataset_empty_tips": "你還沒有知識庫,先創建一個吧", "dataset_search_tool_description": "呼叫「語意搜尋」和「全文搜尋」功能,從「知識庫」中尋找可能與問題相關的參考內容。優先呼叫這個工具來協助回答使用者的問題。", "dataset_select": "可選知識庫", "day": "日", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 7b96ecea5..281ae8aaf 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -782,7 +782,6 @@ "dataset.Manual collection Tip": "手動資料集允許建立一個空的容器來存放資料", "dataset.Move Failed": "移動錯誤", "dataset.Select Dataset": "選擇此知識庫", - "dataset.Select Dataset Tips": "僅能選擇相同索引模型的知識庫", "dataset.Select Folder": "進入資料夾", "dataset.Training Name": "資料訓練", "dataset.collections.Collection Embedding": "{{total}} 個索引", diff --git a/projects/app/src/components/core/app/DatasetSelectModal.tsx b/projects/app/src/components/core/app/DatasetSelectModal.tsx index 377ef7b15..82fc95a4b 100644 --- a/projects/app/src/components/core/app/DatasetSelectModal.tsx +++ b/projects/app/src/components/core/app/DatasetSelectModal.tsx @@ -10,9 +10,10 @@ import { VStack, HStack, IconButton, - Spacer + Spacer, + useDisclosure } from '@chakra-ui/react'; -import { ChevronRightIcon, CloseIcon, InfoIcon } from '@chakra-ui/icons'; +import { ChevronRightIcon, CloseIcon } from '@chakra-ui/icons'; import Avatar from '@fastgpt/web/components/common/Avatar'; import type { SelectedDatasetType } from '@fastgpt/global/core/workflow/type/io'; import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type'; @@ -26,6 +27,7 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { useDatasetSelect } from '@/components/core/dataset/SelectModal'; import FolderPath from '@/components/common/folder/Path'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; +import QuickCreateDatasetModal from '@/pageComponents/app/detail/components/QuickCreateModal'; // Dataset selection modal component export const DatasetSelectModal = ({ @@ -35,19 +37,28 @@ export const DatasetSelectModal = ({ onClose }: { isOpen: boolean; - defaultSelectedDatasets: SelectedDatasetType; - onChange: (e: SelectedDatasetType) => void; + defaultSelectedDatasets: SelectedDatasetType[]; + onChange: (e: SelectedDatasetType[]) => void; onClose: () => void; }) => { // Translation function const { t } = useTranslation(); // Current selected datasets, initialized with defaultSelectedDatasets const [selectedDatasets, setSelectedDatasets] = - useState(defaultSelectedDatasets); + useState(defaultSelectedDatasets); const { toast } = useToast(); // Use server-side search, following the logic of the dataset list page - const { paths, setParentId, searchKey, setSearchKey, datasets, isFetching } = useDatasetSelect(); + const { + paths, + parentId, + setParentId, + searchKey, + setSearchKey, + datasets, + isFetching, + loadDatasets + } = useDatasetSelect(); // The vector model of the first selected dataset const activeVectorModel = selectedDatasets[0]?.vectorModel?.model; @@ -98,7 +109,7 @@ export const DatasetSelectModal = ({ if (isDatasetDisabled(item)) { return toast({ status: 'warning', - title: t('common:dataset.Select Dataset Tips') + title: t('app:dataset.Select_dataset_model_tip') }); } setSelectedDatasets((prev) => [ @@ -115,6 +126,15 @@ export const DatasetSelectModal = ({ } }; + const { + isOpen: isQuickCreateOpen, + onOpen: onOpenQuickCreate, + onClose: onCloseQuickCreate + } = useDisclosure(); + const isRootEmpty = useMemo(() => { + return datasets.length === 0 && paths.length === 0 && !searchKey && !isFetching; + }, [datasets.length, isFetching, paths.length, searchKey]); + // Render component return ( {/* Main vertical layout */} - - {/* Two-column layout */} - - {/* Left: search and dataset list */} - - {/* Search box */} - - setSearchKey(e.target.value?.trim())} - size="md" - /> - - - {/* Path display area - always occupies space, content changes based on search state */} - - {searchKey && ( + + {isRootEmpty ? ( + + + + + ) : ( + <> + {/* Two-column layout */} + + {/* Left: search and dataset list */} + + {/* Search box */} + + setSearchKey(e.target.value?.trim())} + size="md" + /> + + {/* Path display area - always occupies space, content changes based on search state */} - {t('chat:search_results')} - - )} - {!searchKey && paths.length === 0 && ( - // Root directory path - - setParentId('')} - > - {t('common:root_folder')} - - - - )} - {!searchKey && paths.length > 0 && ( - // Subdirectory path - ({ - parentId: path.parentId, - parentName: path.parentName - }))} - FirstPathDom={t('common:root_folder')} - onClick={(e) => setParentId(e)} - /> - )} - - - {/* Dataset list */} - - {datasets.length === 0 && !isFetching && ( - - )} - {datasets.map((item: DatasetListItemType) => ( - - { - if (item.type === DatasetTypeEnum.folder) { - if (searchKey) { - setSearchKey(''); - } - setParentId(item._id); - } else { - onSelect(item, !isDatasetSelected(item._id)); - } - }} - > + {searchKey && ( e.stopPropagation()} // Prevent parent click when clicking checkbox + w="100%" + minH={6} + display="flex" + alignItems="center" + fontSize="sm" + color="myGray.500" > - {item.type !== DatasetTypeEnum.folder && ( - { - const checked = e.target.checked; - onSelect(item, checked); - }} - colorScheme="blue" - size="sm" - /> - )} + {t('chat:search_results')} - - {/* Avatar */} - - - {/* Name and type */} - - - {item.name} + )} + {!searchKey && paths.length === 0 && datasets.length > 0 && ( + // Root directory path + + setParentId('')} + > + {t('common:root_folder')} - - {item.type === DatasetTypeEnum.folder ? ( - <>{t('common:Folder')} - ) : ( - <> - {t('app:Index')}: {item.vectorModel.name} - - )} - - - - {/* Folder expand arrow */} - {item.type === DatasetTypeEnum.folder && ( - - - - )} - + + + )} + {!searchKey && paths.length > 0 && ( + // Subdirectory path + ({ + parentId: path.parentId, + parentName: path.parentName + }))} + FirstPathDom={t('common:root_folder')} + onClick={(e) => setParentId(e)} + /> + )} - ))} - - - {/* Select all / Deselect all */} - {datasets.length > 0 && ( - - { - if (e.target.checked) { - const compatibleDatasets = compatibleDatasetsByModel.filter((dataset) => { - return !isDatasetSelected(dataset._id); - }); - const newSelections = compatibleDatasets.map( - (item: DatasetListItemType) => ({ - datasetId: item._id, - avatar: item.avatar, - name: item.name, - vectorModel: item.vectorModel - }) - ); - setSelectedDatasets((prev) => [...prev, ...newSelections]); - } else { - const datasetIdsToRemove = compatibleDatasetsByModel.map( - (item: DatasetListItemType) => item._id - ); - setSelectedDatasets((prev) => - prev.filter((dataset) => !datasetIdsToRemove.includes(dataset.datasetId)) - ); - } - }} - colorScheme="blue" - size="sm" + {/* Dataset list */} + - {t('common:Select_all')} - + {datasets.length === 0 && !isFetching && ( + + )} + {datasets.map((item: DatasetListItemType) => ( + + { + if (item.type === DatasetTypeEnum.folder) { + if (searchKey) { + setSearchKey(''); + } + setParentId(item._id); + } else { + onSelect(item, !isDatasetSelected(item._id)); + } + }} + > + e.stopPropagation()} // Prevent parent click when clicking checkbox + > + {item.type !== DatasetTypeEnum.folder && ( + { + const checked = e.target.checked; + onSelect(item, checked); + }} + colorScheme="blue" + size="sm" + /> + )} + + + {/* Avatar */} + + + {/* Name and type */} + + + {item.name} + + + {item.type === DatasetTypeEnum.folder ? ( + <>{t('common:Folder')} + ) : ( + <> + {t('app:Index')}: {item.vectorModel.name} + + )} + + + + {/* Folder expand arrow */} + {item.type === DatasetTypeEnum.folder && ( + + + + )} + + + ))} + + + {/* Select all / Deselect all */} + {datasets.length > 0 && ( + + { + if (e.target.checked) { + const compatibleDatasets = compatibleDatasetsByModel.filter( + (dataset) => { + return !isDatasetSelected(dataset._id); + } + ); + const newSelections = compatibleDatasets.map( + (item: DatasetListItemType) => ({ + datasetId: item._id, + avatar: item.avatar, + name: item.name, + vectorModel: item.vectorModel + }) + ); + setSelectedDatasets((prev) => [...prev, ...newSelections]); + } else { + const datasetIdsToRemove = compatibleDatasetsByModel.map( + (item: DatasetListItemType) => item._id + ); + setSelectedDatasets((prev) => + prev.filter( + (dataset) => !datasetIdsToRemove.includes(dataset.datasetId) + ) + ); + } + }} + colorScheme="blue" + size="sm" + > + {t('common:Select_all')} + + + )} - )} - - {/* Right: selected datasets display */} - - {/* Selected count display */} - - {t('app:Selected')}: {selectedDatasets.length} {t('app:dataset')} - - {/* Selected dataset list */} - - {selectedDatasets.length === 0 && !isFetching && ( - - )} - {selectedDatasets.map((item) => ( - - - - {item.name} - - } - size="xs" - variant="ghost" - color="black" - _hover={{ bg: 'myGray.200' }} - onClick={() => - setSelectedDatasets((prev) => - prev.filter((dataset) => dataset.datasetId !== item.datasetId) - ) - } - /> - - ))} - - - + {/* Right: selected datasets display */} + + {!isRootEmpty && ( + <> + {/* Selected count display */} + + {t('app:Selected')}: {selectedDatasets.length} {t('app:dataset')} + + {/* Selected dataset list */} + + {selectedDatasets.length === 0 && !isFetching && ( + + )} + {selectedDatasets.map((item) => ( + + + + {item.name} + + } + size="xs" + variant="ghost" + color="black" + _hover={{ bg: 'myGray.200' }} + onClick={() => + setSelectedDatasets((prev) => + prev.filter((dataset) => dataset.datasetId !== item.datasetId) + ) + } + /> + + ))} + + + )} + + + + )} {/* Modal footer button area */} - - - - - {t('common:dataset.Select Dataset Tips')} - + {!isRootEmpty && ( + )} + + {isRootEmpty ? ( + - + ) : ( + + + + {t('app:dataset.Select_dataset_model_tip')} + + + + )} + + {isQuickCreateOpen && ( + { + setSelectedDatasets((prev) => [...prev, newDataset]); + loadDatasets(); + }} + /> + )} ); }; diff --git a/projects/app/src/components/core/dataset/SelectModal.tsx b/projects/app/src/components/core/dataset/SelectModal.tsx index d83a60ddb..21c7abdcd 100644 --- a/projects/app/src/components/core/dataset/SelectModal.tsx +++ b/projects/app/src/components/core/dataset/SelectModal.tsx @@ -77,7 +77,8 @@ export function useDatasetSelect() { datasets: [], paths: [] }, - loading: isFetching + loading: isFetching, + runAsync: loadDatasets } = useRequest2( async () => { const result = await Promise.all([ @@ -105,7 +106,8 @@ export function useDatasetSelect() { setSearchKey, datasets: data.datasets, paths: data.paths, - isFetching + isFetching, + loadDatasets }; } diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx index 526a55603..32819022d 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectDataset.tsx @@ -36,8 +36,8 @@ export const SelectDatasetRender = React.memo(function SelectDatasetRender({ } = useDisclosure(); const selectedDatasets = useMemo(() => { - if (Array.isArray(item.value)) return item.value as SelectedDatasetType; - return [] as SelectedDatasetType; + if (Array.isArray(item.value)) return item.value as SelectedDatasetType[]; + return [] as SelectedDatasetType[]; }, [item.value]); useEffect(() => { diff --git a/projects/app/src/pageComponents/app/detail/components/QuickCreateModal.tsx b/projects/app/src/pageComponents/app/detail/components/QuickCreateModal.tsx new file mode 100644 index 000000000..24a9fd894 --- /dev/null +++ b/projects/app/src/pageComponents/app/detail/components/QuickCreateModal.tsx @@ -0,0 +1,347 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'next-i18next'; +import { useForm } from 'react-hook-form'; +import { + Box, + Flex, + ModalBody, + ModalFooter, + Button, + FormControl, + Input, + Progress +} from '@chakra-ui/react'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import Avatar from '@fastgpt/web/components/common/Avatar'; +import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; +import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { useUploadAvatar } from '@fastgpt/web/common/file/hooks/useUploadAvatar'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { + postCreateDataset, + getDatasetById, + postCreateDatasetFileCollection +} from '@/web/core/dataset/api'; +import { getUploadAvatarPresignedUrl } from '@/web/common/file/api'; +import { uploadFile2DB } from '@/web/common/file/controller'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { + ChunkSettingModeEnum, + ChunkTriggerConfigTypeEnum, + DataChunkSplitModeEnum, + DatasetCollectionDataProcessModeEnum, + DatasetTypeEnum +} from '@fastgpt/global/core/dataset/constants'; +import { getWebDefaultEmbeddingModel, getWebDefaultLLMModel } from '@/web/common/system/utils'; +import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; +import { getErrText } from '@fastgpt/global/common/error/utils'; +import { formatFileSize } from '@fastgpt/global/common/file/tools'; +import { getFileIcon } from '@fastgpt/global/common/file/icon'; +import type { SelectedDatasetType } from '@fastgpt/global/core/workflow/type/io'; +import type { ImportSourceItemType } from '@/web/core/dataset/type'; +import FileSelector, { + type SelectFileItemType +} from '@/pageComponents/dataset/detail/Import/components/FileSelector'; +import { useRouter } from 'next/router'; + +const QuickCreateDatasetModal = ({ + onClose, + onSuccess, + parentId +}: { + onClose: () => void; + onSuccess: (dataset: SelectedDatasetType) => void; + parentId: string; +}) => { + const { t } = useTranslation(); + const router = useRouter(); + const { defaultModels, embeddingModelList, datasetModelList } = useSystemStore(); + + const defaultVectorModel = + defaultModels.embedding?.model || getWebDefaultEmbeddingModel(embeddingModelList)?.model; + const defaultAgentModel = + defaultModels.datasetTextLLM?.model || getWebDefaultLLMModel(datasetModelList)?.model; + const defaultVLLM = defaultModels.datasetImageLLM?.model; + + const [selectFiles, setSelectFiles] = useState([]); + + const { register, handleSubmit, watch, setValue } = useForm({ + defaultValues: { + parentId, + name: '', + avatar: 'core/dataset/commonDatasetColor' + } + }); + + const avatar = watch('avatar'); + + const { Component: AvatarUploader, handleFileSelectorOpen: handleAvatarSelectorOpen } = + useUploadAvatar(getUploadAvatarPresignedUrl, { + onSuccess: (avatarUrl: string) => { + setValue('avatar', avatarUrl); + } + }); + + const handleSelectFiles = (files: SelectFileItemType[]) => { + setSelectFiles((state) => [ + ...state, + ...files.map((selectFile) => { + const { fileId, file } = selectFile; + + return { + id: fileId, + createStatus: 'waiting', + file, + sourceName: file.name, + sourceSize: formatFileSize(file.size), + icon: getFileIcon(file.name), + uploadedFileRate: 0 + }; + }) + ]); + }; + + const uploadSingleFile = async (fileItem: ImportSourceItemType, datasetId: string) => { + try { + if (!fileItem.file) return; + setSelectFiles((prev) => + prev.map((item) => (item.id === fileItem.id ? { ...item, uploadedFileRate: 0 } : item)) + ); + + const { fileId } = await uploadFile2DB({ + file: fileItem.file, + bucketName: BucketNameEnum.dataset, + data: { datasetId }, + percentListen: (percent) => { + setSelectFiles((prev) => + prev.map((item) => + item.id === fileItem.id + ? { ...item, uploadedFileRate: Math.max(percent, item.uploadedFileRate || 0) } + : item + ) + ); + } + }); + + await postCreateDatasetFileCollection({ + datasetId, + fileId, + trainingType: DatasetCollectionDataProcessModeEnum.chunk, + chunkTriggerType: ChunkTriggerConfigTypeEnum.minSize, + chunkTriggerMinSize: 1000, + chunkSettingMode: ChunkSettingModeEnum.auto, + chunkSplitMode: DataChunkSplitModeEnum.paragraph, + chunkSize: 1024, + indexSize: 512, + customPdfParse: false + }); + + setSelectFiles((prev) => + prev.map((item) => + item.id === fileItem.id ? { ...item, dbFileId: fileId, uploadedFileRate: 100 } : item + ) + ); + } catch (error) { + setSelectFiles((prev) => + prev.map((item) => + item.id === fileItem.id ? { ...item, errorMsg: getErrText(error) } : item + ) + ); + } + }; + + const { runAsync: onCreate, loading: isCreating } = useRequest2( + async (data) => { + const datasetId = await postCreateDataset({ + name: data.name.trim(), + avatar: data.avatar, + intro: '', + parentId, + type: DatasetTypeEnum.dataset, + vectorModel: defaultVectorModel, + agentModel: defaultAgentModel, + vlmModel: defaultVLLM + }); + + if (selectFiles.length > 0) { + await Promise.all(selectFiles.map((file) => uploadSingleFile(file, datasetId))); + } + + const datasetDetail = await getDatasetById(datasetId); + + return { + datasetId, + name: datasetDetail.name, + avatar: datasetDetail.avatar, + vectorModel: datasetDetail.vectorModel + }; + }, + { + manual: true, + successToast: t('app:dataset_create_success'), + onSuccess: (result) => { + onSuccess(result); + onClose(); + setSelectFiles([]); + } + } + ); + + return ( + + + + {t('common:app_icon_and_name')} + + + + + + + + + + + + + + {selectFiles.length > 0 && ( + + {selectFiles.map((item) => ( + + + + {item.sourceName} + + + {item.errorMsg ? ( + + + + {t('common:Error')} + + + + + ) : !!item.uploadedFileRate ? ( + + + {`${item.uploadedFileRate}%`} + + ) : null} + + + {!item.uploadedFileRate && ( + + + setSelectFiles((prev) => + prev.filter((prevItem) => prevItem.id !== item.id) + ) + } + /> + + )} + + + ))} + + )} + + + + + + {t('app:dataset.create_dataset_tips')} + { + router.push('/dataset/list'); + }} + > + {t('common:core.dataset.Dataset')} + + + + + + + + + + + ); +}; + +export default QuickCreateDatasetModal; diff --git a/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/Upload.tsx b/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/Upload.tsx index ea96fc073..36c94bb17 100644 --- a/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/Upload.tsx +++ b/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/Upload.tsx @@ -20,7 +20,6 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { useRouter } from 'next/router'; -import { TabEnum } from '../../../../../pages/dataset/detail/index'; import { postCreateDatasetApiDatasetCollection, postCreateDatasetExternalFileCollection, diff --git a/projects/app/src/service/core/dataset/queues/datasetParse.ts b/projects/app/src/service/core/dataset/queues/datasetParse.ts index 7afc73b5b..5017377d9 100644 --- a/projects/app/src/service/core/dataset/queues/datasetParse.ts +++ b/projects/app/src/service/core/dataset/queues/datasetParse.ts @@ -42,7 +42,7 @@ const requestLLMPargraph = async ({ rawText: string; model: string; billId: string; - paragraphChunkAIMode: ParagraphChunkAIModeEnum; + paragraphChunkAIMode?: ParagraphChunkAIModeEnum; }) => { if ( !global.feConfigs?.isPlus ||