import type { FC, SVGProps } from 'react' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import useSWR from 'swr' import { useRouter } from 'next/navigation' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { omit } from 'lodash-es' import { ArrowRightIcon } from '@heroicons/react/24/solid' import SegmentCard from '../completed/SegmentCard' import { FieldInfo } from '../metadata' import style from '../completed/style.module.css' import { DocumentContext } from '../index' import s from './style.module.css' import cn from '@/utils/classnames' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import { ToastContext } from '@/app/components/base/toast' import type { FullDocumentDetail, ProcessRuleResponse } from '@/models/datasets' import type { CommonResponse } from '@/models/common' import { asyncRunSafe, sleep } from '@/utils' import { fetchIndexingStatus as doFetchIndexingStatus, fetchProcessRule, pauseDocIndexing, resumeDocIndexing } from '@/service/datasets' import StopEmbeddingModal from '@/app/components/datasets/create/stop-embedding-modal' type Props = { detail?: FullDocumentDetail stopPosition?: 'top' | 'bottom' datasetId?: string documentId?: string indexingType?: string detailUpdate: VoidFunction } const StopIcon = ({ className }: SVGProps) => { return } const ResumeIcon = ({ className }: SVGProps) => { return } const RuleDetail: FC<{ sourceData?: ProcessRuleResponse; docName?: string }> = ({ sourceData, docName }) => { const { t } = useTranslation() const segmentationRuleMap = { docName: t('datasetDocuments.embedding.docName'), mode: t('datasetDocuments.embedding.mode'), segmentLength: t('datasetDocuments.embedding.segmentLength'), textCleaning: t('datasetDocuments.embedding.textCleaning'), } const getRuleName = (key: string) => { if (key === 'remove_extra_spaces') return t('datasetCreation.stepTwo.removeExtraSpaces') if (key === 'remove_urls_emails') return t('datasetCreation.stepTwo.removeUrlEmails') if (key === 'remove_stopwords') return t('datasetCreation.stepTwo.removeStopwords') } const getValue = useCallback((field: string) => { let value: string | number | undefined = '-' switch (field) { case 'docName': value = docName break case 'mode': value = sourceData?.mode === 'automatic' ? (t('datasetDocuments.embedding.automatic') as string) : (t('datasetDocuments.embedding.custom') as string) break case 'segmentLength': value = sourceData?.rules?.segmentation?.max_tokens break default: value = sourceData?.mode === 'automatic' ? (t('datasetDocuments.embedding.automatic') as string) // eslint-disable-next-line array-callback-return : sourceData?.rules?.pre_processing_rules?.map((rule) => { if (rule.enabled) return getRuleName(rule.id) }).filter(Boolean).join(';') break } return value }, [sourceData, docName]) return
{Object.keys(segmentationRuleMap).map((field) => { return })}
} const EmbeddingDetail: FC = ({ detail, stopPosition = 'top', datasetId: dstId, documentId: docId, detailUpdate }) => { const onTop = stopPosition === 'top' const { t } = useTranslation() const { notify } = useContext(ToastContext) const { datasetId = '', documentId = '' } = useContext(DocumentContext) const localDatasetId = dstId ?? datasetId const localDocumentId = docId ?? documentId const [indexingStatusDetail, setIndexingStatusDetail] = useState(null) const fetchIndexingStatus = async () => { const status = await doFetchIndexingStatus({ datasetId: localDatasetId, documentId: localDocumentId }) setIndexingStatusDetail(status) return status } const isStopQuery = useRef(false) const stopQueryStatus = useCallback(() => { isStopQuery.current = true }, []) const startQueryStatus = useCallback(async () => { if (isStopQuery.current) return try { const indexingStatusDetail = await fetchIndexingStatus() if (['completed', 'error', 'paused'].includes(indexingStatusDetail?.indexing_status)) { stopQueryStatus() detailUpdate() return } await sleep(2500) await startQueryStatus() } catch (e) { await sleep(2500) await startQueryStatus() } }, [stopQueryStatus]) useEffect(() => { isStopQuery.current = false startQueryStatus() return () => { stopQueryStatus() } }, [startQueryStatus, stopQueryStatus]) const { data: ruleDetail, error: ruleError } = useSWR({ action: 'fetchProcessRule', params: { documentId: localDocumentId }, }, apiParams => fetchProcessRule(omit(apiParams, 'action')), { revalidateOnFocus: false, }) const [showModal, setShowModal] = useState(false) const modalShowHandle = () => setShowModal(true) const modalCloseHandle = () => setShowModal(false) const router = useRouter() const navToDocument = () => { router.push(`/datasets/${localDatasetId}/documents/${localDocumentId}`) } const isEmbedding = useMemo(() => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail]) const isEmbeddingCompleted = useMemo(() => ['completed'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail]) const isEmbeddingPaused = useMemo(() => ['paused'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail]) const isEmbeddingError = useMemo(() => ['error'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail]) const percent = useMemo(() => { const completedCount = indexingStatusDetail?.completed_segments || 0 const totalCount = indexingStatusDetail?.total_segments || 0 if (totalCount === 0) return 0 const percent = Math.round(completedCount * 100 / totalCount) return percent > 100 ? 100 : percent }, [indexingStatusDetail]) const handleSwitch = async () => { const opApi = isEmbedding ? pauseDocIndexing : resumeDocIndexing const [e] = await asyncRunSafe(opApi({ datasetId: localDatasetId, documentId: localDocumentId }) as Promise) if (!e) { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) setIndexingStatusDetail(null) } else { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } } // if (!ruleDetail && !error) // return return ( <>
{isEmbedding && t('datasetDocuments.embedding.processing')} {isEmbeddingCompleted && t('datasetDocuments.embedding.completed')} {isEmbeddingPaused && t('datasetDocuments.embedding.paused')} {isEmbeddingError && t('datasetDocuments.embedding.error')} {onTop && isEmbedding && ( )} {onTop && isEmbeddingPaused && ( )}
{/* progress bar */}
{new Array(10).fill('').map((_, idx) =>
)}
{t('datasetDocuments.embedding.segments')} {indexingStatusDetail?.completed_segments}/{indexingStatusDetail?.total_segments} ยท {percent}%
{!onTop && (
{isEmbedding && ( )} {isEmbeddingPaused && ( )}
)} {onTop && <>
{t('datasetDocuments.embedding.previewTip')}
{[1, 2, 3].map((v, index) => ( ))}
} ) } export default React.memo(EmbeddingDetail)