diff --git a/web/app/components/datasets/create/step-three/index.tsx b/web/app/components/datasets/create/step-three/index.tsx index 10ff33979d..7498fde7fd 100644 --- a/web/app/components/datasets/create/step-three/index.tsx +++ b/web/app/components/datasets/create/step-three/index.tsx @@ -8,6 +8,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets' import AppIcon from '@/app/components/base/app-icon' import Divider from '@/app/components/base/divider' +import { useDocLink } from '@/context/i18n' type StepThreeProps = { datasetId?: string @@ -19,6 +20,7 @@ type StepThreeProps = { const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrievalMethod }: StepThreeProps) => { const { t } = useTranslation() + const docLink = useDocLink() const media = useBreakpoints() const isMobile = media === MediaType.mobile @@ -83,6 +85,14 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
{t('datasetCreation.stepThree.sideTipTitle')}
{t('datasetCreation.stepThree.sideTipContent')}
+ + {t('datasetPipeline.addDocuments.stepThree.learnMore')} + )} diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx index 92fe6f2e19..f5cbac909d 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx @@ -15,6 +15,7 @@ import { useModalContextSelector } from '@/context/modal-context' import Title from './title' import { useGetDataSourceAuth } from '@/service/use-datasource' import Loading from '@/app/components/base/loading' +import { useDocLink } from '@/context/i18n' type OnlineDocumentsProps = { isInPipeline?: boolean @@ -29,6 +30,7 @@ const OnlineDocuments = ({ isInPipeline = false, onCredentialChange, }: OnlineDocumentsProps) => { + const docLink = useDocLink() const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal) const { @@ -125,8 +127,8 @@ const OnlineDocuments = ({ return (
{ + const docLink = useDocLink() const [isInitialMount, setIsInitialMount] = useState(true) const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal) @@ -185,8 +187,8 @@ const OnlineDrive = ({ return (
{ const { t } = useTranslation() + const docLink = useDocLink() const [totalNum, setTotalNum] = useState(0) const [crawledNum, setCrawledNum] = useState(0) const [crawlErrorMessage, setCrawlErrorMessage] = useState('') @@ -151,8 +153,8 @@ const WebsiteCrawl = ({ return (
= ({ )}> {isFullDocMode ? : null}
-
{ event.stopPropagation() toggleCollapse() @@ -162,6 +163,7 @@ const ChildSegmentList: FC = ({ label={`C-${childChunk.position}${edited ? ` · ${t('datasetDocuments.segment.edited')}` : ''}`} text={childChunk.content} onDelete={() => onDelete?.(childChunk.segment_id, childChunk.id)} + className='child-chunk' labelClassName={focused ? 'bg-state-accent-solid text-text-primary-on-surface' : ''} labelInnerClassName={'text-[10px] font-semibold align-bottom leading-6'} contentClassName={cn('!leading-6', focused ? 'bg-state-accent-hover-alt text-text-primary' : 'text-text-secondary')} diff --git a/web/app/components/datasets/documents/detail/completed/common/drawer.tsx b/web/app/components/datasets/documents/detail/completed/common/drawer.tsx new file mode 100644 index 0000000000..806eff01f6 --- /dev/null +++ b/web/app/components/datasets/documents/detail/completed/common/drawer.tsx @@ -0,0 +1,111 @@ +import React, { useCallback, useEffect, useRef } from 'react' +import { createPortal } from 'react-dom' +import cn from '@/utils/classnames' +import { useKeyPress } from 'ahooks' +import { useSegmentListContext } from '..' + +type DrawerProps = { + open: boolean + onClose: () => void + side?: 'right' | 'left' | 'bottom' | 'top' + showOverlay?: boolean + modal?: boolean // click outside event can pass through if modal is false + closeOnOutsideClick?: boolean + panelClassName?: string + panelContentClassName?: string + needCheckChunks?: boolean +} + +const Drawer = ({ + open, + onClose, + side = 'right', + showOverlay = true, + modal = false, + needCheckChunks = false, + children, + panelClassName, + panelContentClassName, +}: React.PropsWithChildren) => { + const panelContentRef = useRef(null) + const currSegment = useSegmentListContext(s => s.currSegment) + const currChildChunk = useSegmentListContext(s => s.currChildChunk) + + useKeyPress('esc', (e) => { + if (!open) return + e.preventDefault() + onClose() + }, { exactMatch: true, useCapture: true }) + + const shouldCloseDrawer = useCallback((target: Node | null) => { + const panelContent = panelContentRef.current + if (!panelContent) return false + const chunks = document.querySelectorAll('.chunk-card') + const childChunks = document.querySelectorAll('.child-chunk') + const isClickOnChunk = Array.from(chunks).some((chunk) => { + return chunk && chunk.contains(target) + }) + const isClickOnChildChunk = Array.from(childChunks).some((chunk) => { + return chunk && chunk.contains(target) + }) + const reopenChunkDetail = (currSegment.showModal && isClickOnChildChunk) + || (currChildChunk.showModal && isClickOnChunk && !isClickOnChildChunk) + return target && !panelContent.contains(target) && (!needCheckChunks || reopenChunkDetail) + }, [currSegment, currChildChunk, needCheckChunks]) + + const onDownCapture = useCallback((e: PointerEvent) => { + if (!open || modal) return + const panelContent = panelContentRef.current + if (!panelContent) return + const target = e.target as Node | null + if (shouldCloseDrawer(target)) + queueMicrotask(onClose) + }, [shouldCloseDrawer, onClose, open, modal]) + + useEffect(() => { + window.addEventListener('pointerdown', onDownCapture, { capture: true }) + return () => + window.removeEventListener('pointerdown', onDownCapture, { capture: true }) + }, [onDownCapture]) + + const isHorizontal = side === 'left' || side === 'right' + + const content = ( +
+ {showOverlay ? ( + + ) + + return open && createPortal(content, document.body) +} + +export default Drawer diff --git a/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx b/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx index cb19aff534..58a9539110 100644 --- a/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx @@ -1,33 +1,42 @@ -import React, { type FC } from 'react' -import Drawer from '@/app/components/base/drawer' -import classNames from '@/utils/classnames' +import React from 'react' +import Drawer from './drawer' +import cn from '@/utils/classnames' import { noop } from 'lodash-es' type IFullScreenDrawerProps = { isOpen: boolean onClose?: () => void fullScreen: boolean - children: React.ReactNode + showOverlay?: boolean + needCheckChunks?: boolean + modal?: boolean } -const FullScreenDrawer: FC = ({ +const FullScreenDrawer = ({ isOpen, onClose = noop, fullScreen, children, -}) => { + showOverlay = true, + needCheckChunks = false, + modal = false, +}: React.PropsWithChildren) => { return ( {children} ) diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx index 894671f79e..726be7519a 100644 --- a/web/app/components/datasets/documents/detail/completed/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/index.tsx @@ -153,7 +153,7 @@ const Completed: FC = ({ return docForm === ChunkingMode.parentChild && parentMode === 'full-doc' }, [docForm, parentMode]) - const { isFetching: isLoadingSegmentList, data: segmentListData } = useSegmentList( + const { isLoading: isLoadingSegmentList, data: segmentListData } = useSegmentList( { datasetId, documentId, @@ -183,7 +183,7 @@ const Completed: FC = ({ } }, [segments]) - const { isFetching: isLoadingChildSegmentList, data: childChunkListData } = useChildSegmentList( + const { isLoading: isLoadingChildSegmentList, data: childChunkListData } = useChildSegmentList( { datasetId, documentId, @@ -664,8 +664,11 @@ const Completed: FC = ({ isOpen={currSegment.showModal} fullScreen={fullScreen} onClose={onCloseSegmentDetail} + showOverlay={false} + needCheckChunks > = ({ isOpen={showNewSegmentModal} fullScreen={fullScreen} onClose={onCloseNewSegmentModal} + modal > = ({ isOpen={currChildChunk.showModal} fullScreen={fullScreen} onClose={onCloseChildSegmentDetail} + showOverlay={false} + needCheckChunks > = ({ isOpen={showNewChildSegmentModal} fullScreen={fullScreen} onClose={onCloseNewChildChunkModal} + modal > = ({ /> {/* Batch Action Buttons */} - {selectedSegmentIds.length > 0 - && 0 && ( + } + /> + )} ) } diff --git a/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx b/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx index 80dced041e..f15f3dbd11 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx @@ -118,7 +118,7 @@ const SegmentCard: FC = ({ return (
{ const { mutateAsync: publishWorkflow } = usePublishWorkflow() const { notify } = useToastContext() const workflowStore = useWorkflowStore() - const { isAllowPublishAsKnowledgePipeline } = useProviderContext() + const { isAllowPublishAsCustomKnowledgePipelineTemplate } = useProviderContext() const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal) const [confirmVisible, { @@ -206,16 +206,16 @@ const Popup = () => { ]) const handleClickPublishAsKnowledgePipeline = useCallback(() => { - if (!isAllowPublishAsKnowledgePipeline) + if (!isAllowPublishAsCustomKnowledgePipelineTemplate) setShowPricingModal() else setShowPublishAsKnowledgePipelineModal() - }, [isAllowPublishAsKnowledgePipeline, setShowPublishAsKnowledgePipelineModal, setShowPricingModal]) + }, [isAllowPublishAsCustomKnowledgePipelineTemplate, setShowPublishAsKnowledgePipelineModal, setShowPricingModal]) return (
@@ -293,12 +293,12 @@ const Popup = () => { onClick={handleClickPublishAsKnowledgePipeline} disabled={!publishedAt || isPublishingAsCustomizedPipeline} > -
+
{t('pipeline.common.publishAs')} - {!isAllowPublishAsKnowledgePipeline && ( + {!isAllowPublishAsCustomKnowledgePipelineTemplate && ( diff --git a/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts b/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts index 66b9214214..cd7a877adf 100644 --- a/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/rag-pipeline/hooks/use-available-nodes-meta-data.ts @@ -25,11 +25,13 @@ export const useAvailableNodesMetaData = () => { dataSourceEmptyDefault, ], []) - const prefixLink = useMemo(() => { + const helpLinkUri = useMemo(() => { if (language === 'zh_Hans') - return 'https://docs.dify.ai/zh-hans/guides/workflow/node/' + return 'https://docs.dify.ai/zh-hans/guides/knowledge-base/knowledge-pipeline/knowledge-pipeline-orchestration#%E6%AD%A5%E9%AA%A4%E4%B8%80%EF%BC%9A%E6%95%B0%E6%8D%AE%E6%BA%90%E9%85%8D%E7%BD%AE' + if (language === 'ja_JP') + return 'https://docs.dify.ai/ja-jp/guides/knowledge-base/knowledge-pipeline/knowledge-pipeline-orchestration#%E3%82%B9%E3%83%86%E3%83%83%E3%83%971%EF%BC%9A%E3%83%87%E3%83%BC%E3%82%BF%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E8%A8%AD%E5%AE%9A' - return 'https://docs.dify.ai/guides/workflow/node/' + return 'https://docs.dify.ai/en/guides/knowledge-base/knowledge-pipeline/knowledge-pipeline-orchestration#step-1%3A-data-source' }, [language]) const availableNodesMetaData = useMemo(() => mergedNodesMetaData.map((node) => { @@ -42,7 +44,7 @@ export const useAvailableNodesMetaData = () => { ...metaData, title, description, - helpLinkUri: `${prefixLink}${metaData.helpLinkUri}`, + helpLinkUri, }, defaultValue: { ...node.defaultValue, @@ -50,7 +52,7 @@ export const useAvailableNodesMetaData = () => { title, }, } - }), [mergedNodesMetaData, t, prefixLink]) + }), [mergedNodesMetaData, t]) const availableNodesMetaDataMap = useMemo(() => availableNodesMetaData.reduce((acc, node) => { acc![node.metaData.type] = node diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx index 81e131b2a2..e1eccaf309 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx @@ -19,8 +19,8 @@ const RerankingModelSelector = ({ readonly = false, }: RerankingModelSelectorProps) => { const { - modelList: rerankModelList, - } = useModelListAndDefaultModel(ModelTypeEnum.rerank) + modelList: rerankModelList, + } = useModelListAndDefaultModel(ModelTypeEnum.rerank) const rerankModel = useMemo(() => { if (!rerankingModel) return undefined diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx index 74629f47ae..40d44f3135 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx @@ -178,11 +178,13 @@ const SearchMethodOption = ({
) } - + {rerankingModelEnabled && ( + + )}
) } diff --git a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts index 215ef85559..d5c810cbc4 100644 --- a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts +++ b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts @@ -114,6 +114,16 @@ export const useConfig = (id: string) => { }) }, [getNodeData, handleNodeDataUpdate]) + const handleRerankingModelEnabledChange = useCallback((rerankingModelEnabled: boolean) => { + const nodeData = getNodeData() + handleNodeDataUpdate({ + retrieval_model: { + ...nodeData?.data.retrieval_model, + reranking_enable: rerankingModelEnabled, + }, + }) + }, [getNodeData, handleNodeDataUpdate]) + const handleWeighedScoreChange = useCallback((weightedScore: { value: number[] }) => { const nodeData = getNodeData() handleNodeDataUpdate({ @@ -190,6 +200,7 @@ export const useConfig = (id: string) => { handleEmbeddingModelChange, handleRetrievalSearchMethodChange, handleHybridSearchModeChange, + handleRerankingModelEnabledChange, handleWeighedScoreChange, handleRerankingModelChange, handleTopKChange, diff --git a/web/app/components/workflow/nodes/knowledge-base/panel.tsx b/web/app/components/workflow/nodes/knowledge-base/panel.tsx index 7014e12558..9eaf4ef39f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/panel.tsx @@ -39,6 +39,7 @@ const Panel: FC> = ({ handleEmbeddingModelChange, handleRetrievalSearchMethodChange, handleHybridSearchModeChange, + handleRerankingModelEnabledChange, handleWeighedScoreChange, handleRerankingModelChange, handleTopKChange, @@ -150,6 +151,8 @@ const Panel: FC> = ({ onHybridSearchModeChange={handleHybridSearchModeChange} weightedScore={data.retrieval_model.weights} onWeightedScoreChange={handleWeighedScoreChange} + rerankingModelEnabled={data.retrieval_model.reranking_enable} + onRerankingModelEnabledChange={handleRerankingModelEnabledChange} rerankingModel={data.retrieval_model.reranking_model} onRerankingModelChange={handleRerankingModelChange} topK={data.retrieval_model.top_k} diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 902e5baf99..09019d3024 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -61,7 +61,7 @@ type ProviderContextState = { }, refreshLicenseLimit: () => void isAllowTransferWorkspace: boolean - isAllowPublishAsKnowledgePipeline: boolean + isAllowPublishAsCustomKnowledgePipelineTemplate: boolean } const ProviderContext = createContext({ modelProviders: [], @@ -108,7 +108,7 @@ const ProviderContext = createContext({ }, refreshLicenseLimit: noop, isAllowTransferWorkspace: false, - isAllowPublishAsKnowledgePipeline: false, + isAllowPublishAsCustomKnowledgePipelineTemplate: false, }) export const useProviderContext = () => useContext(ProviderContext) @@ -147,7 +147,7 @@ export const ProviderContextProvider = ({ const [isEducationWorkspace, setIsEducationWorkspace] = useState(false) const { data: educationAccountInfo, isLoading: isLoadingEducationAccountInfo, isFetching: isFetchingEducationAccountInfo } = useEducationStatus(!enableEducationPlan) const [isAllowTransferWorkspace, setIsAllowTransferWorkspace] = useState(false) - const [isAllowPublishAsKnowledgePipeline, setIsAllowPublishAsKnowledgePipeline] = useState(false) + const [isAllowPublishAsCustomKnowledgePipelineTemplate, setIsAllowPublishAsCustomKnowledgePipelineTemplate] = useState(false) const fetchPlan = async () => { try { @@ -179,7 +179,7 @@ export const ProviderContextProvider = ({ if (data.is_allow_transfer_workspace) setIsAllowTransferWorkspace(data.is_allow_transfer_workspace) if (data.knowledge_pipeline?.publish_enabled) - setIsAllowPublishAsKnowledgePipeline(data.knowledge_pipeline?.publish_enabled) + setIsAllowPublishAsCustomKnowledgePipelineTemplate(data.knowledge_pipeline?.publish_enabled) } catch (error) { console.error('Failed to fetch plan info:', error) @@ -245,7 +245,7 @@ export const ProviderContextProvider = ({ licenseLimit, refreshLicenseLimit: fetchPlan, isAllowTransferWorkspace, - isAllowPublishAsKnowledgePipeline, + isAllowPublishAsCustomKnowledgePipelineTemplate, }}> {children} diff --git a/web/i18n/en-US/pipeline.ts b/web/i18n/en-US/pipeline.ts index e906d615f7..4b29bdbb00 100644 --- a/web/i18n/en-US/pipeline.ts +++ b/web/i18n/en-US/pipeline.ts @@ -1,7 +1,7 @@ const translation = { common: { goToAddDocuments: 'Go to add documents', - publishAs: 'Publish as a Knowledge Pipeline', + publishAs: 'Publish as a Customized Pipeline Template', confirmPublish: 'Confirm Publish', confirmPublishContent: 'After successfully publishing the knowledge pipeline, the chunk structure of this knowledge base cannot be modified. Are you sure you want to publish it?', publishAsPipeline: { diff --git a/web/i18n/zh-Hans/pipeline.ts b/web/i18n/zh-Hans/pipeline.ts index 0bba075196..3c3a7a6506 100644 --- a/web/i18n/zh-Hans/pipeline.ts +++ b/web/i18n/zh-Hans/pipeline.ts @@ -1,7 +1,7 @@ const translation = { common: { goToAddDocuments: '去添加文档', - publishAs: '发布为知识流水线', + publishAs: '发布为自定义流水线模板', confirmPublish: '确认发布', confirmPublishContent: '成功发布知识流水线后,此知识库的分段结构将无法修改。您确定要发布吗?', publishAsPipeline: {