| @@ -1,5 +1,6 @@ | |||
| import { forwardRef, useEffect, useRef } from 'react' | |||
| import cn from 'classnames' | |||
| import { sleep } from '@/utils' | |||
| type IProps = { | |||
| placeholder?: string | |||
| @@ -32,14 +33,13 @@ const AutoHeightTextarea = forwardRef( | |||
| return false | |||
| } | |||
| const focus = () => { | |||
| const focus = async () => { | |||
| if (!doFocus()) { | |||
| let hasFocus = false | |||
| const runId = setInterval(() => { | |||
| hasFocus = doFocus() | |||
| if (hasFocus) | |||
| clearInterval(runId) | |||
| }, 100) | |||
| await sleep(100) | |||
| hasFocus = doFocus() | |||
| if (!hasFocus) | |||
| focus() | |||
| } | |||
| } | |||
| @@ -21,6 +21,7 @@ import UpgradeBtn from '@/app/components/billing/upgrade-btn' | |||
| import { useProviderContext } from '@/context/provider-context' | |||
| import TooltipPlus from '@/app/components/base/tooltip-plus' | |||
| import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' | |||
| import { sleep } from '@/utils' | |||
| type Props = { | |||
| datasetId: string | |||
| @@ -95,38 +96,41 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index | |||
| return status.data | |||
| } | |||
| // const [_, setRunId, getRunId] = useGetState<ReturnType<typeof setInterval>>() | |||
| const [runId, setRunId] = useState<ReturnType<typeof setInterval>>() | |||
| const runIdRef = useRef(runId) | |||
| const getRunId = () => runIdRef.current | |||
| const [isStopQuery, setIsStopQuery] = useState(false) | |||
| const isStopQueryRef = useRef(isStopQuery) | |||
| useEffect(() => { | |||
| runIdRef.current = runId | |||
| }, [runId]) | |||
| isStopQueryRef.current = isStopQuery | |||
| }, [isStopQuery]) | |||
| const stopQueryStatus = () => { | |||
| clearInterval(getRunId()) | |||
| setRunId(undefined) | |||
| setIsStopQuery(true) | |||
| } | |||
| const startQueryStatus = () => { | |||
| const runId = setInterval(async () => { | |||
| // It's so strange that the interval can't be cleared after the clearInterval called. And the runId is current. | |||
| if (!getRunId()) | |||
| return | |||
| const startQueryStatus = async () => { | |||
| if (isStopQueryRef.current) | |||
| return | |||
| try { | |||
| const indexingStatusBatchDetail = await fetchIndexingStatus() | |||
| const isCompleted = indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error'].includes(indexingStatusDetail.indexing_status)) | |||
| if (isCompleted) | |||
| const isCompleted = indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error', 'paused'].includes(indexingStatusDetail.indexing_status)) | |||
| if (isCompleted) { | |||
| stopQueryStatus() | |||
| }, 2500) | |||
| setRunId(runId) | |||
| return | |||
| } | |||
| await sleep(2500) | |||
| await startQueryStatus() | |||
| } | |||
| catch (e) { | |||
| await sleep(2500) | |||
| await startQueryStatus() | |||
| } | |||
| } | |||
| useEffect(() => { | |||
| fetchIndexingStatus() | |||
| startQueryStatus() | |||
| return () => { | |||
| stopQueryStatus() | |||
| } | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []) | |||
| // get rule | |||
| @@ -154,7 +158,7 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index | |||
| return indexingStatusBatchDetail.some(indexingStatusDetail => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || '')) | |||
| }, [indexingStatusBatchDetail]) | |||
| const isEmbeddingCompleted = useMemo(() => { | |||
| return indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error'].includes(indexingStatusDetail?.indexing_status || '')) | |||
| return indexingStatusBatchDetail.every(indexingStatusDetail => ['completed', 'error', 'paused'].includes(indexingStatusDetail?.indexing_status || '')) | |||
| }, [indexingStatusBatchDetail]) | |||
| const getSourceName = (id: string) => { | |||
| @@ -1,12 +1,11 @@ | |||
| import type { FC, SVGProps } from 'react' | |||
| import React, { useCallback, useEffect, useMemo, useState } 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 { useGetState } from 'ahooks' | |||
| import cn from 'classnames' | |||
| import SegmentCard from '../completed/SegmentCard' | |||
| import { FieldInfo } from '../metadata' | |||
| @@ -18,7 +17,7 @@ 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 } from '@/utils' | |||
| import { asyncRunSafe, sleep } from '@/utils' | |||
| import { formatNumber } from '@/utils/format' | |||
| import { fetchIndexingStatus as doFetchIndexingStatus, fetchIndexingEstimate, fetchProcessRule, pauseDocIndexing, resumeDocIndexing } from '@/service/datasets' | |||
| import DatasetDetailContext from '@/context/dataset-detail' | |||
| @@ -120,45 +119,49 @@ const EmbeddingDetail: FC<Props> = ({ detail, stopPosition = 'top', datasetId: d | |||
| const localDocumentId = docId ?? documentId | |||
| const localIndexingTechnique = indexingType ?? indexingTechnique | |||
| const [indexingStatusDetail, setIndexingStatusDetail, getIndexingStatusDetail] = useGetState<any>(null) | |||
| const [indexingStatusDetail, setIndexingStatusDetail] = useState<any>(null) | |||
| const fetchIndexingStatus = async () => { | |||
| try { | |||
| const status = await doFetchIndexingStatus({ datasetId: localDatasetId, documentId: localDocumentId }) | |||
| setIndexingStatusDetail(status) | |||
| // eslint-disable-next-line @typescript-eslint/no-use-before-define | |||
| startQueryStatus() | |||
| } | |||
| catch (err) { | |||
| // eslint-disable-next-line @typescript-eslint/no-use-before-define | |||
| stopQueryStatus() | |||
| notify({ type: 'error', message: `error: ${err}` }) | |||
| } | |||
| const status = await doFetchIndexingStatus({ datasetId: localDatasetId, documentId: localDocumentId }) | |||
| setIndexingStatusDetail(status) | |||
| return status | |||
| } | |||
| const [runId, setRunId, getRunId] = useGetState<any>(null) | |||
| const [isStopQuery, setIsStopQuery] = useState(false) | |||
| const isStopQueryRef = useRef(isStopQuery) | |||
| useEffect(() => { | |||
| isStopQueryRef.current = isStopQuery | |||
| }, [isStopQuery]) | |||
| const stopQueryStatus = () => { | |||
| clearInterval(getRunId()) | |||
| setIsStopQuery(true) | |||
| } | |||
| const startQueryStatus = () => { | |||
| const runId = setInterval(() => { | |||
| const indexingStatusDetail = getIndexingStatusDetail() | |||
| if (indexingStatusDetail?.indexing_status === 'completed') { | |||
| const startQueryStatus = async () => { | |||
| if (isStopQueryRef.current) | |||
| return | |||
| try { | |||
| const indexingStatusDetail = await fetchIndexingStatus() | |||
| if (['completed', 'error', 'paused'].includes(indexingStatusDetail?.indexing_status)) { | |||
| stopQueryStatus() | |||
| detailUpdate() | |||
| return | |||
| } | |||
| fetchIndexingStatus() | |||
| }, 2500) | |||
| setRunId(runId) | |||
| await sleep(2500) | |||
| await startQueryStatus() | |||
| } | |||
| catch (e) { | |||
| await sleep(2500) | |||
| await startQueryStatus() | |||
| } | |||
| } | |||
| useEffect(() => { | |||
| fetchIndexingStatus() | |||
| setIsStopQuery(false) | |||
| startQueryStatus() | |||
| return () => { | |||
| stopQueryStatus() | |||
| } | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []) | |||
| const { data: indexingEstimateDetail, error: indexingEstimateErr } = useSWR({ | |||
| @@ -300,4 +303,4 @@ const EmbeddingDetail: FC<Props> = ({ detail, stopPosition = 'top', datasetId: d | |||
| ) | |||
| } | |||
| export default EmbeddingDetail | |||
| export default React.memo(EmbeddingDetail) | |||
| @@ -17,6 +17,7 @@ import type { ModerationService } from '@/models/common' | |||
| import { TransferMethod, type VisionFile, type VisionSettings } from '@/types/app' | |||
| import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types' | |||
| import type { WorkflowProcess } from '@/app/components/base/chat/types' | |||
| import { sleep } from '@/utils' | |||
| export type IResultProps = { | |||
| isWorkflow: boolean | |||
| @@ -179,16 +180,16 @@ const Result: FC<IResultProps> = ({ | |||
| onShowRes() | |||
| setRespondingTrue() | |||
| const startTime = Date.now() | |||
| let isTimeout = false | |||
| const runId = setInterval(() => { | |||
| if (Date.now() - startTime > 1000 * 60) { // 1min timeout | |||
| clearInterval(runId) | |||
| let isEnd = false | |||
| let isTimeout = false; | |||
| (async () => { | |||
| await sleep(1000 * 60) // 1min timeout | |||
| if (!isEnd) { | |||
| setRespondingFalse() | |||
| onCompleted(getCompletionRes(), taskId, false) | |||
| isTimeout = true | |||
| } | |||
| }, 1000) | |||
| })() | |||
| if (isWorkflow) { | |||
| sendWorkflowMessage( | |||
| @@ -234,7 +235,7 @@ const Result: FC<IResultProps> = ({ | |||
| notify({ type: 'error', message: data.error }) | |||
| setRespondingFalse() | |||
| onCompleted(getCompletionRes(), taskId, false) | |||
| clearInterval(runId) | |||
| isEnd = true | |||
| return | |||
| } | |||
| setWorkflowProccessData(produce(getWorkflowProccessData()!, (draft) => { | |||
| @@ -249,7 +250,7 @@ const Result: FC<IResultProps> = ({ | |||
| setRespondingFalse() | |||
| setMessageId(tempMessageId) | |||
| onCompleted(getCompletionRes(), taskId, true) | |||
| clearInterval(runId) | |||
| isEnd = true | |||
| }, | |||
| }, | |||
| isInstalledApp, | |||
| @@ -269,7 +270,7 @@ const Result: FC<IResultProps> = ({ | |||
| setRespondingFalse() | |||
| setMessageId(tempMessageId) | |||
| onCompleted(getCompletionRes(), taskId, true) | |||
| clearInterval(runId) | |||
| isEnd = true | |||
| }, | |||
| onMessageReplace: (messageReplace) => { | |||
| res = [messageReplace.answer] | |||
| @@ -280,7 +281,7 @@ const Result: FC<IResultProps> = ({ | |||
| return | |||
| setRespondingFalse() | |||
| onCompleted(getCompletionRes(), taskId, false) | |||
| clearInterval(runId) | |||
| isEnd = true | |||
| }, | |||
| }, isInstalledApp, installedAppInfo?.id) | |||
| } | |||