diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks.ts
new file mode 100644
index 0000000000..1837466f79
--- /dev/null
+++ b/web/app/components/datasets/documents/create-from-pipeline/hooks.ts
@@ -0,0 +1,21 @@
+import { useTranslation } from 'react-i18next'
+import { AddDocumentsStep } from './types'
+
+export const useAddDocumentsSteps = () => {
+ const { t } = useTranslation()
+ const steps = [
+ {
+ label: t('datasetPipeline.addDocuments.steps.chooseDatasource'),
+ value: AddDocumentsStep.dataSource,
+ },
+ {
+ label: t('datasetPipeline.addDocuments.steps.ProcessDocuments'),
+ value: AddDocumentsStep.processDocuments,
+ },
+ {
+ label: t('datasetPipeline.addDocuments.steps.ProcessingDocuments'),
+ value: AddDocumentsStep.processingDocuments,
+ },
+ ]
+ return steps
+}
diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
new file mode 100644
index 0000000000..7736b07eda
--- /dev/null
+++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
@@ -0,0 +1,217 @@
+'use client'
+import { useCallback, useMemo, useState } from 'react'
+// import StepIndicator from './step-indicator'
+// import { useTestRunSteps } from './hooks'
+// import DataSourceOptions from './data-source-options'
+import type { CrawlResultItem, FileItem } from '@/models/datasets'
+import { DataSourceType } from '@/models/datasets'
+// import LocalFile from './data-source/local-file'
+import produce from 'immer'
+import { useProviderContextSelector } from '@/context/provider-context'
+import { DataSourceProvider, type NotionPage } from '@/models/common'
+// import Notion from './data-source/notion'
+import VectorSpaceFull from '@/app/components/billing/vector-space-full'
+// import Firecrawl from './data-source/website/firecrawl'
+// import JinaReader from './data-source/website/jina-reader'
+// import WaterCrawl from './data-source/website/water-crawl'
+// import Actions from './data-source/actions'
+// import DocumentProcessing from './document-processing'
+import { useTranslation } from 'react-i18next'
+import type { Datasource } from '@/app/components/rag-pipeline/components/panel/test-run/types'
+import LocalFile from '@/app/components/rag-pipeline/components/panel/test-run/data-source/local-file'
+import Notion from '@/app/components/rag-pipeline/components/panel/test-run/data-source/notion'
+import FireCrawl from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/firecrawl'
+import JinaReader from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/jina-reader'
+import WaterCrawl from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/water-crawl'
+import Actions from '@/app/components/rag-pipeline/components/panel/test-run/data-source/actions'
+import DocumentProcessing from '@/app/components/rag-pipeline/components/panel/test-run/document-processing'
+import LeftHeader from './left-header'
+// import { usePipelineRun } from '../../../hooks'
+// import type { Datasource } from './types'
+
+const TestRunPanel = () => {
+ const { t } = useTranslation()
+ const [currentStep, setCurrentStep] = useState(1)
+ const [datasource, setDatasource] = useState
()
+ const [fileList, setFiles] = useState([])
+ const [notionPages, setNotionPages] = useState([])
+ const [websitePages, setWebsitePages] = useState([])
+ const [websiteCrawlJobId, setWebsiteCrawlJobId] = useState('')
+
+ const plan = useProviderContextSelector(state => state.plan)
+ const enableBilling = useProviderContextSelector(state => state.enableBilling)
+
+ // const steps = useTestRunSteps()
+
+ const allFileLoaded = (fileList.length > 0 && fileList.every(file => file.file.id))
+ const isVectorSpaceFull = plan.usage.vectorSpace >= plan.total.vectorSpace
+ const isShowVectorSpaceFull = allFileLoaded && isVectorSpaceFull && enableBilling
+ const notSupportBatchUpload = enableBilling && plan.type === 'sandbox'
+ const nextDisabled = useMemo(() => {
+ if (!fileList.length)
+ return true
+ if (fileList.some(file => !file.file.id))
+ return true
+ return isShowVectorSpaceFull
+ }, [fileList, isShowVectorSpaceFull])
+
+ const nextBtnDisabled = useMemo(() => {
+ if (!datasource) return true
+ if (datasource.type === DataSourceType.FILE)
+ return nextDisabled
+ if (datasource.type === DataSourceType.NOTION)
+ return isShowVectorSpaceFull || !notionPages.length
+ if (datasource.type === DataSourceProvider.fireCrawl
+ || datasource.type === DataSourceProvider.jinaReader
+ || datasource.type === DataSourceProvider.waterCrawl)
+ return isShowVectorSpaceFull || !websitePages.length
+ return false
+ }, [datasource, nextDisabled, isShowVectorSpaceFull, notionPages.length, websitePages.length])
+
+ const updateFile = (fileItem: FileItem, progress: number, list: FileItem[]) => {
+ const newList = produce(list, (draft) => {
+ const targetIndex = draft.findIndex(file => file.fileID === fileItem.fileID)
+ draft[targetIndex] = {
+ ...draft[targetIndex],
+ progress,
+ }
+ })
+ setFiles(newList)
+ }
+
+ const updateFileList = (preparedFiles: FileItem[]) => {
+ setFiles(preparedFiles)
+ }
+
+ const updateNotionPages = (value: NotionPage[]) => {
+ setNotionPages(value)
+ }
+
+ const handleNextStep = useCallback(() => {
+ setCurrentStep(preStep => preStep + 1)
+ }, [])
+
+ const handleBackStep = useCallback(() => {
+ setCurrentStep(preStep => preStep - 1)
+ }, [])
+
+ // const { handleRun } = usePipelineRun()
+
+ const handleProcess = useCallback((data: Record) => {
+ if (!datasource)
+ return
+ const datasourceInfo: Record = {}
+ let datasource_type = ''
+ if (datasource.type === DataSourceType.FILE) {
+ datasource_type = 'local_file'
+ datasourceInfo.fileId = fileList.map(file => file.fileID)
+ }
+ if (datasource.type === DataSourceType.NOTION) {
+ datasource_type = 'online_document'
+ datasourceInfo.workspaceId = notionPages[0].workspace_id
+ datasourceInfo.page = notionPages.map((page) => {
+ const { workspace_id, ...rest } = page
+ return rest
+ })
+ }
+ if (datasource.type === DataSourceProvider.fireCrawl
+ || datasource.type === DataSourceProvider.jinaReader
+ || datasource.type === DataSourceProvider.waterCrawl) {
+ datasource_type = 'website_crawl'
+ datasourceInfo.jobId = websiteCrawlJobId
+ datasourceInfo.result = websitePages
+ }
+ // handleRun({
+ // inputs: data,
+ // datasource_type,
+ // datasource_info: datasourceInfo,
+ // })
+ }, [datasource, fileList, notionPages, websiteCrawlJobId, websitePages])
+
+ return (
+
+
+
+
+ {
+ currentStep === 1 && (
+ <>
+
+ {/* */}
+ {datasource?.type === DataSourceType.FILE && (
+
+ )}
+ {datasource?.type === DataSourceType.NOTION && (
+
+ )}
+ {datasource?.type === DataSourceProvider.fireCrawl && (
+
+ )}
+ {datasource?.type === DataSourceProvider.jinaReader && (
+
+ )}
+ {datasource?.type === DataSourceProvider.waterCrawl && (
+
+ )}
+ {isShowVectorSpaceFull && (
+
+ )}
+
+
+ >
+ )
+ }
+ {
+ currentStep === 2 && (
+
+ )
+ }
+
+
+ {/* Preview */}
+
+
+
+ )
+}
+
+export default TestRunPanel
diff --git a/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx b/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx
new file mode 100644
index 0000000000..77bb00fc5a
--- /dev/null
+++ b/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx
@@ -0,0 +1,46 @@
+import React from 'react'
+import { RiArrowLeftLine } from '@remixicon/react'
+import Button from '@/app/components/base/button'
+import { useParams } from 'next/navigation'
+import Effect from '@/app/components/base/effect'
+import { useAddDocumentsSteps } from './hooks'
+import StepIndicator from './step-indicator'
+
+type LeftHeaderProps = {
+ title: string
+ currentStep: number
+}
+
+const LeftHeader = ({
+ title,
+ currentStep,
+}: LeftHeaderProps) => {
+ const { datasetId } = useParams()
+ const steps = useAddDocumentsSteps()
+
+ return (
+
+
+
+ {title}
+
+ /
+
+
+
+ {steps[currentStep - 1]?.label}
+
+
+
+
+
+
+ )
+}
+
+export default React.memo(LeftHeader)
diff --git a/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx b/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx
new file mode 100644
index 0000000000..9db7a0446d
--- /dev/null
+++ b/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx
@@ -0,0 +1,33 @@
+import cn from '@/utils/classnames'
+import React from 'react'
+
+type Step = {
+ label: string
+ value: string
+}
+
+type StepIndicatorProps = {
+ currentStep: number
+ steps: Step[]
+}
+
+const StepIndicator = ({
+ currentStep,
+ steps,
+}: StepIndicatorProps) => {
+ return (
+
+ {steps.map((step, index) => {
+ const isActive = index === currentStep - 1
+ return (
+
+ )
+ })}
+
+ )
+}
+
+export default React.memo(StepIndicator)
diff --git a/web/app/components/datasets/documents/create-from-pipeline/types.tsx b/web/app/components/datasets/documents/create-from-pipeline/types.tsx
new file mode 100644
index 0000000000..06c7f7ae09
--- /dev/null
+++ b/web/app/components/datasets/documents/create-from-pipeline/types.tsx
@@ -0,0 +1,5 @@
+export enum AddDocumentsStep {
+ dataSource = 'dataSource',
+ processDocuments = 'processDocuments',
+ processingDocuments = 'processingDocuments',
+}
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 584a60bd9a..74360bda0c 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
@@ -1,7 +1,7 @@
import React, { type FC, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
-import { StatusItem } from '../../../list'
+import StatusItem from '../../../status-item'
import { useDocumentContext } from '../../index'
import ChildSegmentList from '../child-segment-list'
import Tag from '../common/tag'
@@ -228,15 +228,15 @@ const SegmentCard: FC = ({
}
{
isParagraphMode && child_chunks.length > 0
- &&
+ &&
}
{showModal
&& = ({ datasetId, documentId }) => {
datasetId={datasetId}
onUpdate={handleOperate}
/>
- = ({ datasetId }) => {
const [currPage, setCurrPage] = React.useState(0)
const [limit, setLimit] = useState(DEFAULT_LIMIT)
const router = useRouter()
- const { dataset } = useDatasetDetailContext()
+ const dataset = useDatasetDetailContextWithSelector(s => s.dataset)
const [notionPageSelectorModalVisible, setNotionPageSelectorModalVisible] = useState(false)
const [timerCanRun, setTimerCanRun] = useState(true)
const isDataSourceNotion = dataset?.data_source_type === DataSourceType.NOTION
@@ -172,6 +172,11 @@ const Documents: FC = ({ datasetId }) => {
const total = documentsRes?.total || 0
const routeToDocCreate = () => {
+ // if dataset is create from pipeline, redirect to create from pipeline page
+ if (dataset?.pipeline_id) {
+ router.push(`/datasets/${datasetId}/documents/create-from-pipeline`)
+ return
+ }
if (isDataSourceNotion) {
setNotionPageSelectorModalVisible(true)
return
@@ -267,7 +272,7 @@ const Documents: FC = ({ datasetId }) => {
? 'https://docs.dify.ai/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
: 'https://docs.dify.ai/en/guides/knowledge-base/integrate-knowledge-within-application'
}
- >
+ >
{t('datasetDocuments.list.learnMore')}
diff --git a/web/i18n/en-US/dataset-pipeline.ts b/web/i18n/en-US/dataset-pipeline.ts
index 5665d42d0b..3fc6b336d1 100644
--- a/web/i18n/en-US/dataset-pipeline.ts
+++ b/web/i18n/en-US/dataset-pipeline.ts
@@ -70,6 +70,15 @@ const translation = {
addInputField: 'Add Input Field',
editInputField: 'Edit Input Field',
},
+ addDocuments: {
+ title: 'Add Documents',
+ steps: {
+ chooseDatasource: 'Choose a Data Source',
+ processDocuments: 'Process Documents',
+ processingDocuments: 'Processing Documents',
+ },
+ backToDataSource: 'Data Source',
+ },
}
export default translation
diff --git a/web/i18n/zh-Hans/dataset-pipeline.ts b/web/i18n/zh-Hans/dataset-pipeline.ts
index aae121bcaf..670d97a18f 100644
--- a/web/i18n/zh-Hans/dataset-pipeline.ts
+++ b/web/i18n/zh-Hans/dataset-pipeline.ts
@@ -70,6 +70,15 @@ const translation = {
addInputField: '添加输入字段',
editInputField: '编辑输入字段',
},
+ addDocuments: {
+ title: '添加文档',
+ steps: {
+ chooseDatasource: '选择数据源',
+ processDocuments: '处理文档',
+ processingDocuments: '正在处理文档',
+ },
+ backToDataSource: '数据源',
+ },
}
export default translation
diff --git a/web/models/pipeline.ts b/web/models/pipeline.ts
index fe501a9216..efd6129488 100644
--- a/web/models/pipeline.ts
+++ b/web/models/pipeline.ts
@@ -1,4 +1,4 @@
-import type { Edge, Node, SupportUploadFileTypes } from '@/app/components/workflow/types'
+import type { Edge, EnvironmentVariable, Node, SupportUploadFileTypes } from '@/app/components/workflow/types'
import type { DSLImportMode, DSLImportStatus } from './app'
import type { ChunkingMode, DatasetPermission, IconInfo } from './datasets'
import type { Dependency } from '@/app/components/plugins/types'
@@ -143,3 +143,30 @@ export type PipelineDatasourceNodeRunRequest = {
}
export type PipelineDatasourceNodeRunResponse = Record
+
+export type PublishedPipelineInfoResponse = {
+ id: string
+ graph: {
+ nodes: Node[]
+ edges: Edge[]
+ viewport: Viewport
+ }
+ created_at: number
+ created_by: {
+ id: string
+ name: string
+ email: string
+ }
+ hash: string
+ updated_at: number
+ updated_by: {
+ id: string
+ name: string
+ email: string
+ },
+ environment_variables?: EnvironmentVariable[]
+ rag_pipeline_variables?: RAGPipelineVariables
+ version: string
+ marked_name: string
+ marked_comment: string
+}
diff --git a/web/service/use-pipeline.ts b/web/service/use-pipeline.ts
index 9c33e9c278..9b1215ab8a 100644
--- a/web/service/use-pipeline.ts
+++ b/web/service/use-pipeline.ts
@@ -15,6 +15,7 @@ import type {
PipelineTemplateByIdResponse,
PipelineTemplateListParams,
PipelineTemplateListResponse,
+ PublishedPipelineInfoResponse,
UpdateTemplateInfoRequest,
UpdateTemplateInfoResponse,
} from '@/models/pipeline'
@@ -156,3 +157,12 @@ export const useDataSourceList = (enabled?: boolean) => {
retry: false,
})
}
+
+export const usePublishedPipelineInfo = (pipelineId: string) => {
+ return useQuery({
+ queryKey: [NAME_SPACE, 'published-pipeline', pipelineId],
+ queryFn: () => {
+ return get(`/rag/pipelines/${pipelineId}/workflows/publish`)
+ },
+ })
+}
diff --git a/web/tailwind-common-config.ts b/web/tailwind-common-config.ts
index 16279aa1b6..fc03cb321d 100644
--- a/web/tailwind-common-config.ts
+++ b/web/tailwind-common-config.ts
@@ -124,6 +124,7 @@ const config = {
'tag-selector-mask-bg': 'var(--color-tag-selector-mask-bg)',
'tag-selector-mask-hover-bg': 'var(--color-tag-selector-mask-hover-bg)',
'pipeline-template-card-hover-bg': 'var(--color-pipeline-template-card-hover-bg)',
+ 'pipeline-add-documents-title-bg': 'var(--color-pipeline-add-documents-title-bg)',
},
animation: {
'spin-slow': 'spin 2s linear infinite',
diff --git a/web/themes/manual-dark.css b/web/themes/manual-dark.css
index aa8a62511e..4a837a6f66 100644
--- a/web/themes/manual-dark.css
+++ b/web/themes/manual-dark.css
@@ -67,4 +67,5 @@ html[data-theme="dark"] {
--color-tag-selector-mask-bg: linear-gradient(90deg, rgba(34, 34, 37, 0) 0%, rgba(34, 34, 37, 1) 100%);
--color-tag-selector-mask-hover-bg: linear-gradient(90deg, rgba(39, 39, 43, 0) 0%, rgba(39, 39, 43, 1) 100%);
--color-pipeline-template-card-hover-bg: linear-gradient(0deg, rgba(58, 58, 64, 1) 60.27%, rgba(58, 58, 64, 0) 100%);
+ --color-pipeline-add-documents-title-bg: linear-gradient(92deg, rgba(54, 191, 250, 1) 0%, rgba(41, 109, 255, 1) 97.78%);
}
diff --git a/web/themes/manual-light.css b/web/themes/manual-light.css
index f2838041c2..fb5cf3f3fe 100644
--- a/web/themes/manual-light.css
+++ b/web/themes/manual-light.css
@@ -67,4 +67,5 @@ html[data-theme="light"] {
--color-tag-selector-mask-bg: linear-gradient(90deg, rgba(252, 252, 253, 0) 0%, rgba(252, 252, 253, 1) 100%);
--color-tag-selector-mask-hover-bg: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
--color-pipeline-template-card-hover-bg: linear-gradient(0deg, rgba(249, 250, 251, 1) 60.27%, rgba(249, 250, 251, 0) 100%);
+ --color-pipeline-add-documents-title-bg: linear-gradient(92deg, rgba(11, 165, 236, 0.95) 0%, rgba(21, 90, 239, 0.95) 97.78%);
}