You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. 'use client'
  2. import { useCallback, useMemo, useState } from 'react'
  3. import DataSourceOptions from './data-source-options'
  4. import type { CrawlResultItem, CustomFile as File, FileItem } from '@/models/datasets'
  5. import { DataSourceType } from '@/models/datasets'
  6. import LocalFile from '@/app/components/rag-pipeline/components/panel/test-run/data-source/local-file'
  7. import produce from 'immer'
  8. import { useProviderContextSelector } from '@/context/provider-context'
  9. import { DataSourceProvider, type NotionPage } from '@/models/common'
  10. import Notion from '@/app/components/rag-pipeline/components/panel/test-run/data-source/notion'
  11. import VectorSpaceFull from '@/app/components/billing/vector-space-full'
  12. import FireCrawl from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/firecrawl'
  13. import JinaReader from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/jina-reader'
  14. import WaterCrawl from '@/app/components/rag-pipeline/components/panel/test-run/data-source/website/water-crawl'
  15. import Actions from './data-source/actions'
  16. import DocumentProcessing from '@/app/components/rag-pipeline/components/panel/test-run/document-processing'
  17. import { useTranslation } from 'react-i18next'
  18. import type { Datasource } from '@/app/components/rag-pipeline/components/panel/test-run/types'
  19. import LeftHeader from './left-header'
  20. import { usePublishedPipelineInfo } from '@/service/use-pipeline'
  21. import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
  22. import Loading from '@/app/components/base/loading'
  23. import type { Node } from '@/app/components/workflow/types'
  24. import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
  25. import FilePreview from './preview/file-preview'
  26. import NotionPagePreview from './preview/notion-page-preview'
  27. import WebsitePreview from './preview/web-preview'
  28. const TestRunPanel = () => {
  29. const { t } = useTranslation()
  30. const [currentStep, setCurrentStep] = useState(1)
  31. const [datasource, setDatasource] = useState<Datasource>()
  32. const [fileList, setFiles] = useState<FileItem[]>([])
  33. const [notionPages, setNotionPages] = useState<NotionPage[]>([])
  34. const [websitePages, setWebsitePages] = useState<CrawlResultItem[]>([])
  35. const [websiteCrawlJobId, setWebsiteCrawlJobId] = useState('')
  36. const [currentFile, setCurrentFile] = useState<File | undefined>()
  37. const [currentNotionPage, setCurrentNotionPage] = useState<NotionPage | undefined>()
  38. const [currentWebsite, setCurrentWebsite] = useState<CrawlResultItem | undefined>()
  39. const plan = useProviderContextSelector(state => state.plan)
  40. const enableBilling = useProviderContextSelector(state => state.enableBilling)
  41. const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id)
  42. const { data: pipelineInfo, isFetching: isFetchingPipelineInfo } = usePublishedPipelineInfo(pipelineId || '')
  43. const allFileLoaded = (fileList.length > 0 && fileList.every(file => file.file.id))
  44. const isVectorSpaceFull = plan.usage.vectorSpace >= plan.total.vectorSpace
  45. const isShowVectorSpaceFull = allFileLoaded && isVectorSpaceFull && enableBilling
  46. const notSupportBatchUpload = enableBilling && plan.type === 'sandbox'
  47. const nextDisabled = useMemo(() => {
  48. if (!fileList.length)
  49. return true
  50. if (fileList.some(file => !file.file.id))
  51. return true
  52. return isShowVectorSpaceFull
  53. }, [fileList, isShowVectorSpaceFull])
  54. const nextBtnDisabled = useMemo(() => {
  55. if (!datasource) return true
  56. if (datasource.type === DataSourceType.FILE)
  57. return nextDisabled
  58. if (datasource.type === DataSourceType.NOTION)
  59. return isShowVectorSpaceFull || !notionPages.length
  60. if (datasource.type === DataSourceProvider.fireCrawl
  61. || datasource.type === DataSourceProvider.jinaReader
  62. || datasource.type === DataSourceProvider.waterCrawl)
  63. return isShowVectorSpaceFull || !websitePages.length
  64. return false
  65. }, [datasource, nextDisabled, isShowVectorSpaceFull, notionPages.length, websitePages.length])
  66. const updateFile = (fileItem: FileItem, progress: number, list: FileItem[]) => {
  67. const newList = produce(list, (draft) => {
  68. const targetIndex = draft.findIndex(file => file.fileID === fileItem.fileID)
  69. draft[targetIndex] = {
  70. ...draft[targetIndex],
  71. progress,
  72. }
  73. })
  74. setFiles(newList)
  75. }
  76. const updateFileList = useCallback((preparedFiles: FileItem[]) => {
  77. setFiles(preparedFiles)
  78. }, [])
  79. const updateNotionPages = useCallback((value: NotionPage[]) => {
  80. setNotionPages(value)
  81. }, [])
  82. const updateCurrentFile = useCallback((file: File) => {
  83. setCurrentFile(file)
  84. }, [])
  85. const hideFilePreview = useCallback(() => {
  86. setCurrentFile(undefined)
  87. }, [])
  88. const updateCurrentPage = useCallback((page: NotionPage) => {
  89. setCurrentNotionPage(page)
  90. }, [])
  91. const hideNotionPagePreview = useCallback(() => {
  92. setCurrentNotionPage(undefined)
  93. }, [])
  94. const updateCurrentWebsite = useCallback((website: CrawlResultItem) => {
  95. setCurrentWebsite(website)
  96. }, [])
  97. const hideWebsitePreview = useCallback(() => {
  98. setCurrentWebsite(undefined)
  99. }, [])
  100. const handleNextStep = useCallback(() => {
  101. setCurrentStep(preStep => preStep + 1)
  102. }, [])
  103. const handleBackStep = useCallback(() => {
  104. setCurrentStep(preStep => preStep - 1)
  105. }, [])
  106. const handleProcess = useCallback((data: Record<string, any>) => {
  107. if (!datasource)
  108. return
  109. const datasourceInfo: Record<string, any> = {}
  110. let datasource_type = ''
  111. if (datasource.type === DataSourceType.FILE) {
  112. datasource_type = 'local_file'
  113. datasourceInfo.fileId = fileList.map(file => file.fileID)
  114. }
  115. if (datasource.type === DataSourceType.NOTION) {
  116. datasource_type = 'online_document'
  117. datasourceInfo.workspaceId = notionPages[0].workspace_id
  118. datasourceInfo.page = notionPages.map((page) => {
  119. const { workspace_id, ...rest } = page
  120. return rest
  121. })
  122. }
  123. if (datasource.type === DataSourceProvider.fireCrawl
  124. || datasource.type === DataSourceProvider.jinaReader
  125. || datasource.type === DataSourceProvider.waterCrawl) {
  126. datasource_type = 'website_crawl'
  127. datasourceInfo.jobId = websiteCrawlJobId
  128. datasourceInfo.result = websitePages
  129. }
  130. // todo: Run Pipeline
  131. console.log('datasource_type', datasource_type)
  132. }, [datasource, fileList, notionPages, websiteCrawlJobId, websitePages])
  133. if (isFetchingPipelineInfo) {
  134. return (
  135. <Loading type='app' />
  136. )
  137. }
  138. return (
  139. <div
  140. className='relative flex h-[calc(100vh-56px)] min-w-[1512px] rounded-t-2xl border-t border-effects-highlight bg-background-default-subtle'
  141. >
  142. <div className='flex flex-1 flex-col px-14'>
  143. <LeftHeader
  144. title={t('datasetPipeline.addDocuments.title')}
  145. currentStep={currentStep}
  146. />
  147. <div className='grow overflow-y-auto'>
  148. {
  149. currentStep === 1 && (
  150. <div className='flex flex-col gap-y-5 pt-4'>
  151. <DataSourceOptions
  152. datasourceNodeId={datasource?.nodeId || ''}
  153. onSelect={setDatasource}
  154. pipelineNodes={(pipelineInfo?.graph.nodes || []) as Node<DataSourceNodeType>[]}
  155. />
  156. {datasource?.type === DataSourceType.FILE && (
  157. <LocalFile
  158. files={fileList}
  159. updateFile={updateFile}
  160. updateFileList={updateFileList}
  161. onPreview={updateCurrentFile}
  162. notSupportBatchUpload={notSupportBatchUpload}
  163. />
  164. )}
  165. {datasource?.type === DataSourceType.NOTION && (
  166. <Notion
  167. nodeId={datasource?.nodeId || ''}
  168. notionPages={notionPages}
  169. updateNotionPages={updateNotionPages}
  170. canPreview
  171. onPreview={updateCurrentPage}
  172. />
  173. )}
  174. {datasource?.type === DataSourceProvider.fireCrawl && (
  175. <FireCrawl
  176. nodeId={datasource?.nodeId || ''}
  177. variables={datasource?.variables}
  178. checkedCrawlResult={websitePages}
  179. onCheckedCrawlResultChange={setWebsitePages}
  180. onJobIdChange={setWebsiteCrawlJobId}
  181. onPreview={updateCurrentWebsite}
  182. />
  183. )}
  184. {datasource?.type === DataSourceProvider.jinaReader && (
  185. <JinaReader
  186. nodeId={datasource?.nodeId || ''}
  187. variables={datasource?.variables}
  188. checkedCrawlResult={websitePages}
  189. onCheckedCrawlResultChange={setWebsitePages}
  190. onJobIdChange={setWebsiteCrawlJobId}
  191. onPreview={updateCurrentWebsite}
  192. />
  193. )}
  194. {datasource?.type === DataSourceProvider.waterCrawl && (
  195. <WaterCrawl
  196. nodeId={datasource?.nodeId || ''}
  197. variables={datasource?.variables}
  198. checkedCrawlResult={websitePages}
  199. onCheckedCrawlResultChange={setWebsitePages}
  200. onJobIdChange={setWebsiteCrawlJobId}
  201. onPreview={updateCurrentWebsite}
  202. />
  203. )}
  204. {isShowVectorSpaceFull && (
  205. <VectorSpaceFull />
  206. )}
  207. <Actions disabled={nextBtnDisabled} handleNextStep={handleNextStep} />
  208. </div>
  209. )
  210. }
  211. {
  212. currentStep === 2 && (
  213. <DocumentProcessing
  214. dataSourceNodeId={datasource?.nodeId || ''}
  215. onProcess={handleProcess}
  216. onBack={handleBackStep}
  217. />
  218. )
  219. }
  220. </div>
  221. </div>
  222. {/* Preview */}
  223. <div className='flex h-full flex-1 shrink-0 flex-col pl-2 pt-2'>
  224. {
  225. currentStep === 1 && (
  226. <>
  227. {currentFile && <FilePreview file={currentFile} hidePreview={hideFilePreview} />}
  228. {currentNotionPage && <NotionPagePreview currentPage={currentNotionPage} hidePreview={hideNotionPagePreview} />}
  229. {currentWebsite && <WebsitePreview payload={currentWebsite} hidePreview={hideWebsitePreview} />}
  230. </>
  231. )
  232. }
  233. </div>
  234. </div>
  235. )
  236. }
  237. export default TestRunPanel