refactor: Update knowledge pipeline terminology and chunk detail UI/UX (#25749)

This commit is contained in:
Wu Tianwei 2025-09-16 12:16:36 +08:00 committed by GitHub
commit 1e9fce50a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 221 additions and 56 deletions

View File

@ -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
</div>
<div className='text-base font-semibold text-text-secondary'>{t('datasetCreation.stepThree.sideTipTitle')}</div>
<div className='text-text-tertiary'>{t('datasetCreation.stepThree.sideTipContent')}</div>
<a
href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')}
target='_blank'
rel='noreferrer noopener'
className='system-sm-regular text-text-accent'
>
{t('datasetPipeline.addDocuments.stepThree.learnMore')}
</a>
</div>
</div>
)}

View File

@ -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 (
<div className='flex flex-col gap-y-2'>
<Header
docTitle='How to use?'
docLink='https://docs.dify.ai'
docTitle='Docs'
docLink={docLink('/guides/knowledge-base/knowledge-pipeline/authorize-data-source')}
onClickConfiguration={handleSetting}
pluginName={nodeData.datasource_label}
currentCredentialId={currentCredentialId}

View File

@ -14,6 +14,7 @@ import produce from 'immer'
import { useShallow } from 'zustand/react/shallow'
import { useModalContextSelector } from '@/context/modal-context'
import { useGetDataSourceAuth } from '@/service/use-datasource'
import { useDocLink } from '@/context/i18n'
type OnlineDriveProps = {
nodeId: string
@ -28,6 +29,7 @@ const OnlineDrive = ({
isInPipeline = false,
onCredentialChange,
}: OnlineDriveProps) => {
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 (
<div className='flex flex-col gap-y-2'>
<Header
docTitle='Online Drive Docs'
docLink='https://docs.dify.ai/'
docTitle='Docs'
docLink={docLink('/guides/knowledge-base/knowledge-pipeline/authorize-data-source')}
onClickConfiguration={handleSetting}
pluginName={nodeData.datasource_label}
currentCredentialId={currentCredentialId}

View File

@ -25,6 +25,7 @@ import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store'
import { useShallow } from 'zustand/react/shallow'
import { useModalContextSelector } from '@/context/modal-context'
import { useGetDataSourceAuth } from '@/service/use-datasource'
import { useDocLink } from '@/context/i18n'
const I18N_PREFIX = 'datasetCreation.stepOne.website'
@ -42,6 +43,7 @@ const WebsiteCrawl = ({
onCredentialChange,
}: WebsiteCrawlProps) => {
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 (
<div className='flex flex-col'>
<Header
docTitle='How to use?'
docLink='https://docs.dify.ai'
docTitle='Docs'
docLink={docLink('/guides/knowledge-base/knowledge-pipeline/authorize-data-source')}
onClickConfiguration={handleSetting}
pluginName={nodeData.datasource_label}
currentCredentialId={currentCredentialId}

View File

@ -95,12 +95,13 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
)}>
{isFullDocMode ? <Divider type='horizontal' className='my-1 h-px bg-divider-subtle' /> : null}
<div className={cn('flex items-center justify-between', isFullDocMode ? 'sticky -top-2 left-0 bg-background-default pb-3 pt-2' : '')}>
<div className={cn(
'flex h-7 items-center rounded-lg pl-1 pr-3',
isParagraphMode && 'cursor-pointer',
(isParagraphMode && collapsed) && 'bg-dataset-child-chunk-expand-btn-bg',
isFullDocMode && 'pl-0',
)}
<div
className={cn(
'flex h-7 items-center rounded-lg pl-1 pr-3',
isParagraphMode && 'cursor-pointer',
(isParagraphMode && collapsed) && 'bg-dataset-child-chunk-expand-btn-bg',
isFullDocMode && 'pl-0',
)}
onClick={(event) => {
event.stopPropagation()
toggleCollapse()
@ -162,6 +163,7 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
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')}

View File

@ -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<DrawerProps>) => {
const panelContentRef = useRef<HTMLDivElement>(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 = (
<div className='pointer-events-none fixed inset-0 z-[9999]'>
{showOverlay ? (
<div
onClick={modal ? onClose : undefined}
aria-hidden='true'
className={cn(
'fixed inset-0 bg-black/30 opacity-0 transition-opacity duration-200 ease-in',
open && 'opacity-100',
modal && open ? 'pointer-events-auto' : 'pointer-events-none',
)}
/>
) : null}
{/* Drawer panel */}
<div
role='dialog'
aria-modal={modal ? 'true' : 'false'}
className={cn(
'pointer-events-auto fixed flex flex-col',
side === 'right' && 'right-0',
side === 'left' && 'left-0',
side === 'bottom' && 'bottom-0',
side === 'top' && 'top-0',
isHorizontal ? 'h-screen' : 'w-screen',
panelClassName,
)}
>
<div ref={panelContentRef} className={cn('flex grow flex-col', panelContentClassName)}>
{children}
</div>
</div>
</div>
)
return open && createPortal(content, document.body)
}
export default Drawer

View File

@ -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<IFullScreenDrawerProps> = ({
const FullScreenDrawer = ({
isOpen,
onClose = noop,
fullScreen,
children,
}) => {
showOverlay = true,
needCheckChunks = false,
modal = false,
}: React.PropsWithChildren<IFullScreenDrawerProps>) => {
return (
<Drawer
isOpen={isOpen}
open={isOpen}
onClose={onClose}
panelClassName={classNames('bg-components-panel-bg !p-0',
panelClassName={cn(
fullScreen
? '!w-full !max-w-full'
: 'mb-2 mr-2 mt-16 !w-[560px] !max-w-[560px] rounded-xl border-[0.5px] border-components-panel-border',
? 'w-full'
: 'w-[560px] pb-2 pr-2 pt-16',
)}
mask={false}
unmount
footer={null}
panelContentClassName={cn(
'bg-components-panel-bg',
!fullScreen && 'rounded-xl border-[0.5px] border-components-panel-border',
)}
showOverlay={showOverlay}
needCheckChunks={needCheckChunks}
modal={modal}
>
{children}
</Drawer>)

View File

@ -153,7 +153,7 @@ const Completed: FC<ICompletedProps> = ({
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<ICompletedProps> = ({
}
}, [segments])
const { isFetching: isLoadingChildSegmentList, data: childChunkListData } = useChildSegmentList(
const { isLoading: isLoadingChildSegmentList, data: childChunkListData } = useChildSegmentList(
{
datasetId,
documentId,
@ -664,8 +664,11 @@ const Completed: FC<ICompletedProps> = ({
isOpen={currSegment.showModal}
fullScreen={fullScreen}
onClose={onCloseSegmentDetail}
showOverlay={false}
needCheckChunks
>
<SegmentDetail
key={currSegment.segInfo?.id}
segInfo={currSegment.segInfo ?? { id: '' }}
docForm={docForm}
isEditMode={currSegment.isEditMode}
@ -678,6 +681,7 @@ const Completed: FC<ICompletedProps> = ({
isOpen={showNewSegmentModal}
fullScreen={fullScreen}
onClose={onCloseNewSegmentModal}
modal
>
<NewSegment
docForm={docForm}
@ -691,8 +695,11 @@ const Completed: FC<ICompletedProps> = ({
isOpen={currChildChunk.showModal}
fullScreen={fullScreen}
onClose={onCloseChildSegmentDetail}
showOverlay={false}
needCheckChunks
>
<ChildSegmentDetail
key={currChildChunk.childChunkInfo?.id}
chunkId={currChunkId}
childChunkInfo={currChildChunk.childChunkInfo ?? { id: '' }}
docForm={docForm}
@ -705,6 +712,7 @@ const Completed: FC<ICompletedProps> = ({
isOpen={showNewChildSegmentModal}
fullScreen={fullScreen}
onClose={onCloseNewChildChunkModal}
modal
>
<NewChildSegment
chunkId={currChunkId}
@ -714,15 +722,16 @@ const Completed: FC<ICompletedProps> = ({
/>
</FullScreenDrawer>
{/* Batch Action Buttons */}
{selectedSegmentIds.length > 0
&& <BatchAction
{selectedSegmentIds.length > 0 && (
<BatchAction
className='absolute bottom-16 left-0 z-20'
selectedIds={selectedSegmentIds}
onBatchEnable={onChangeSwitch.bind(null, true, '')}
onBatchDisable={onChangeSwitch.bind(null, false, '')}
onBatchDelete={onDelete.bind(null, '')}
onCancel={onCancelBatchOperation}
/>}
/>
)}
</SegmentListContext.Provider>
)
}

View File

@ -118,7 +118,7 @@ const SegmentCard: FC<ISegmentCardProps> = ({
return (
<div
className={cn(
'group/card w-full rounded-xl px-3',
'chunk-card group/card w-full rounded-xl px-3',
isFullDocMode ? '' : 'pb-2 pt-2.5 hover:bg-dataset-chunk-detail-card-hover-bg',
focused.segmentContent ? 'bg-dataset-chunk-detail-card-hover-bg' : '',
className,

View File

@ -62,7 +62,7 @@ const Popup = () => {
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 (
<div className={classNames(
'rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5',
isAllowPublishAsKnowledgePipeline ? 'w-[320px] ' : 'w-[360px]',
isAllowPublishAsCustomKnowledgePipelineTemplate ? 'w-[360px]' : 'w-[400px]',
)}>
<div className='p-4 pt-3'>
<div className='system-xs-medium-uppercase flex h-6 items-center text-text-tertiary'>
@ -293,12 +293,12 @@ const Popup = () => {
onClick={handleClickPublishAsKnowledgePipeline}
disabled={!publishedAt || isPublishingAsCustomizedPipeline}
>
<div className='flex grow items-center gap-x-2'>
<div className='flex grow items-center gap-x-2 overflow-hidden'>
<RiHammerLine className='h-4 w-4 shrink-0' />
<span className='grow truncate text-left' title={t('pipeline.common.publishAs')}>
{t('pipeline.common.publishAs')}
</span>
{!isAllowPublishAsKnowledgePipeline && (
{!isAllowPublishAsCustomKnowledgePipelineTemplate && (
<PremiumBadge className='shrink-0 cursor-pointer select-none' size='s' color='indigo'>
<SparklesSoft className='flex size-3 items-center text-components-premium-badge-indigo-text-stop-0' />
<span className='system-2xs-medium p-0.5'>

View File

@ -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

View File

@ -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

View File

@ -178,11 +178,13 @@ const SearchMethodOption = ({
</div>
)
}
<RerankingModelSelector
rerankingModel={rerankingModel}
onRerankingModelChange={onRerankingModelChange}
readonly={readonly}
/>
{rerankingModelEnabled && (
<RerankingModelSelector
rerankingModel={rerankingModel}
onRerankingModelChange={onRerankingModelChange}
readonly={readonly}
/>
)}
</div>
)
}

View File

@ -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,

View File

@ -39,6 +39,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
handleEmbeddingModelChange,
handleRetrievalSearchMethodChange,
handleHybridSearchModeChange,
handleRerankingModelEnabledChange,
handleWeighedScoreChange,
handleRerankingModelChange,
handleTopKChange,
@ -150,6 +151,8 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({
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}

View File

@ -61,7 +61,7 @@ type ProviderContextState = {
},
refreshLicenseLimit: () => void
isAllowTransferWorkspace: boolean
isAllowPublishAsKnowledgePipeline: boolean
isAllowPublishAsCustomKnowledgePipelineTemplate: boolean
}
const ProviderContext = createContext<ProviderContextState>({
modelProviders: [],
@ -108,7 +108,7 @@ const ProviderContext = createContext<ProviderContextState>({
},
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}
</ProviderContext.Provider>

View File

@ -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: {

View File

@ -1,7 +1,7 @@
const translation = {
common: {
goToAddDocuments: '去添加文档',
publishAs: '发布为知识流水线',
publishAs: '发布为自定义流水线模板',
confirmPublish: '确认发布',
confirmPublishContent: '成功发布知识流水线后,此知识库的分段结构将无法修改。您确定要发布吗?',
publishAsPipeline: {