import React, { useEffect, useMemo, useRef, useState } from 'react' import useSWR from 'swr' import { useRouter } from 'next/navigation' import { useTranslation } from 'react-i18next' import { omit } from 'lodash-es' import { ArrowRightIcon } from '@heroicons/react/24/solid' import { RiCheckboxCircleFill, RiErrorWarningFill, RiLoader2Fill, RiTerminalBoxLine, } from '@remixicon/react' import cn from '@/utils/classnames' import Button from '@/app/components/base/button' import type { IndexingStatusResponse } from '@/models/datasets' import { fetchIndexingStatusBatch as doFetchIndexingStatus, fetchProcessRule } from '@/service/datasets' import NotionIcon from '@/app/components/base/notion-icon' import PriorityLabel from '@/app/components/billing/priority-label' import { Plan } from '@/app/components/billing/type' import { ZapFast } from '@/app/components/base/icons/src/vender/solid/general' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import { useProviderContext } from '@/context/provider-context' import { sleep } from '@/utils' import Tooltip from '@/app/components/base/tooltip' import { useInvalidDocumentList } from '@/service/knowledge/use-document' import DocumentFileIcon from '@/app/components/datasets/common/document-file-icon' import RuleDetail from './rule-detail' import type { IndexingType } from '@/app/components/datasets/create/step-two' import type { RETRIEVE_METHOD } from '@/types/app' import { DatasourceType, type InitialDocumentDetail } from '@/models/pipeline' type EmbeddingProcessProps = { datasetId: string batchId: string documents?: InitialDocumentDetail[] indexingType?: IndexingType retrievalMethod?: RETRIEVE_METHOD } const EmbeddingProcess = ({ datasetId, batchId, documents = [], indexingType, retrievalMethod, }: EmbeddingProcessProps) => { const { t } = useTranslation() const { enableBilling, plan } = useProviderContext() const firstDocument = documents[0] const [indexingStatusBatchDetail, setIndexingStatusDetail] = useState([]) const fetchIndexingStatus = async () => { const status = await doFetchIndexingStatus({ datasetId, batchId }) setIndexingStatusDetail(status.data) return status.data } const [isStopQuery, setIsStopQuery] = useState(false) const isStopQueryRef = useRef(isStopQuery) useEffect(() => { isStopQueryRef.current = isStopQuery }, [isStopQuery]) const stopQueryStatus = () => { setIsStopQuery(true) } const startQueryStatus = async () => { if (isStopQueryRef.current) return try { const indexingStatusBatchDetail = await fetchIndexingStatus() const isCompleted = indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error', 'paused'].includes(indexingStatusDetail.indexing_status)) if (isCompleted) { stopQueryStatus() return } await sleep(2500) await startQueryStatus() } catch { await sleep(2500) await startQueryStatus() } } useEffect(() => { setIsStopQuery(false) startQueryStatus() return () => { stopQueryStatus() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // get rule const { data: ruleDetail } = useSWR({ action: 'fetchProcessRule', params: { documentId: firstDocument.id }, }, apiParams => fetchProcessRule(omit(apiParams, 'action')), { revalidateOnFocus: false, }) const router = useRouter() const invalidDocumentList = useInvalidDocumentList() const navToDocumentList = () => { invalidDocumentList() router.push(`/datasets/${datasetId}/documents`) } const navToApiDocs = () => { router.push('/datasets?category=api') } const isEmbedding = useMemo(() => { return indexingStatusBatchDetail.some(indexingStatusDetail => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || '')) }, [indexingStatusBatchDetail]) const isEmbeddingCompleted = useMemo(() => { return indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error', 'paused'].includes(indexingStatusDetail?.indexing_status || '')) }, [indexingStatusBatchDetail]) const getSourceName = (id: string) => { const doc = documents.find(document => document.id === id) return doc?.name } const getFileType = (name?: string) => name?.split('.').pop() || 'txt' const getSourcePercent = (detail: IndexingStatusResponse) => { const completedCount = detail.completed_segments || 0 const totalCount = detail.total_segments || 0 if (totalCount === 0) return 0 const percent = Math.round(completedCount * 100 / totalCount) return percent > 100 ? 100 : percent } const getSourceType = (id: string) => { const doc = documents.find(document => document.id === id) return doc?.data_source_type } const getIcon = (id: string) => { const doc = documents.find(document => document.id === id) return doc?.data_source_info.notion_page_icon } const isSourceEmbedding = (detail: IndexingStatusResponse) => ['indexing', 'splitting', 'parsing', 'cleaning', 'waiting'].includes(detail.indexing_status || '') return ( <>
{isEmbedding &&
{t('datasetDocuments.embedding.processing')}
} {isEmbeddingCompleted && t('datasetDocuments.embedding.completed')}
{ enableBilling && plan.type !== Plan.team && (
{t('billing.plansCommon.documentProcessingPriorityUpgrade')}
) }
{indexingStatusBatchDetail.map(indexingStatusDetail => (
{isSourceEmbedding(indexingStatusDetail) && (
)}
{getSourceType(indexingStatusDetail.id) === DatasourceType.localFile && ( )} {getSourceType(indexingStatusDetail.id) === DatasourceType.onlineDocument && ( )}
{getSourceName(indexingStatusDetail.id)}
{ enableBilling && ( ) }
{isSourceEmbedding(indexingStatusDetail) && (
{`${getSourcePercent(indexingStatusDetail)}%`}
)} {indexingStatusDetail.indexing_status === 'error' && ( )} {indexingStatusDetail.indexing_status === 'completed' && ( )}
))}

) } export default EmbeddingProcess