diff --git a/web/src/components/chunk-method-dialog/index.tsx b/web/src/components/chunk-method-dialog/index.tsx index 36d168040..d35ddd48c 100644 --- a/web/src/components/chunk-method-dialog/index.tsx +++ b/web/src/components/chunk-method-dialog/index.tsx @@ -51,7 +51,7 @@ import RaptorFormFields, { import { ButtonLoading } from '../ui/button'; import { Input } from '../ui/input'; import { DynamicPageRange } from './dynamic-page-range'; -import { useFetchParserListOnMount, useShowAutoKeywords } from './hooks'; +import { useShowAutoKeywords } from './hooks'; import { useDefaultParserValues, useFillDefaultValueOnMount, @@ -93,8 +93,6 @@ export function ChunkMethodDialog({ }: IProps) { const { t } = useTranslation(); - const { parserList } = useFetchParserListOnMount(documentExtension); - const { data: knowledgeDetails } = useFetchKnowledgeBaseConfiguration(); const useGraphRag = useMemo(() => { @@ -105,48 +103,58 @@ export function ChunkMethodDialog({ const fillDefaultParserValue = useFillDefaultValueOnMount(); - const FormSchema = z.object({ - parseType: z.number(), - parser_id: z - .string() - .min(1, { - message: t('common.pleaseSelect'), - }) - .trim(), - pipeline_id: z.string().optional(), - parser_config: z.object({ - task_page_size: z.coerce.number().optional(), - layout_recognize: z.string().optional(), - chunk_token_num: z.coerce.number().optional(), - delimiter: z.string().optional(), - auto_keywords: z.coerce.number().optional(), - auto_questions: z.coerce.number().optional(), - html4excel: z.boolean().optional(), - raptor: z - .object({ - use_raptor: z.boolean().optional(), - prompt: z.string().optional().optional(), - max_token: z.coerce.number().optional(), - threshold: z.coerce.number().optional(), - max_cluster: z.coerce.number().optional(), - random_seed: z.coerce.number().optional(), + const FormSchema = z + .object({ + parseType: z.number(), + parser_id: z + .string() + .min(1, { + message: t('common.pleaseSelect'), }) - .optional(), - graphrag: z.object({ - use_graphrag: z.boolean().optional(), + .trim(), + pipeline_id: z.string().optional(), + parser_config: z.object({ + task_page_size: z.coerce.number().optional(), + layout_recognize: z.string().optional(), + chunk_token_num: z.coerce.number().optional(), + delimiter: z.string().optional(), + auto_keywords: z.coerce.number().optional(), + auto_questions: z.coerce.number().optional(), + html4excel: z.boolean().optional(), + raptor: z + .object({ + use_raptor: z.boolean().optional(), + prompt: z.string().optional().optional(), + max_token: z.coerce.number().optional(), + threshold: z.coerce.number().optional(), + max_cluster: z.coerce.number().optional(), + random_seed: z.coerce.number().optional(), + }) + .optional(), + graphrag: z.object({ + use_graphrag: z.boolean().optional(), + }), + entity_types: z.array(z.string()).optional(), + pages: z + .array(z.object({ from: z.coerce.number(), to: z.coerce.number() })) + .optional(), }), - entity_types: z.array(z.string()).optional(), - pages: z - .array(z.object({ from: z.coerce.number(), to: z.coerce.number() })) - .optional(), - }), - }); + }) + .superRefine((data, ctx) => { + if (data.parseType === 2 && !data.pipeline_id) { + ctx.addIssue({ + path: ['pipeline_id'], + message: t('common.pleaseSelect'), + code: 'custom', + }); + } + }); const form = useForm>({ resolver: zodResolver(FormSchema), defaultValues: { - parser_id: parserId, - pipeline_id: pipelineId, + parser_id: parserId || '', + pipeline_id: pipelineId || '', parseType: pipelineId ? 2 : 1, parser_config: defaultParserValues, }, @@ -209,8 +217,8 @@ export function ChunkMethodDialog({ const pages = parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? []; form.reset({ - parser_id: parserId, - pipeline_id: pipelineId, + parser_id: parserId || '', + pipeline_id: pipelineId || '', parseType: pipelineId ? 2 : 1, parser_config: fillDefaultParserValue({ pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }], @@ -231,13 +239,14 @@ export function ChunkMethodDialog({ knowledgeDetails.parser_config, parserConfig, parserId, + pipelineId, useGraphRag, visible, ]); const parseType = useWatch({ control: form.control, name: 'parseType', - defaultValue: 1, + defaultValue: pipelineId ? 2 : 1, }); return ( diff --git a/web/src/components/ui/radio.tsx b/web/src/components/ui/radio.tsx index 915056cf8..5db0fca43 100644 --- a/web/src/components/ui/radio.tsx +++ b/web/src/components/ui/radio.tsx @@ -49,7 +49,7 @@ function Radio({ value, checked, disabled, onChange, children }: RadioProps) { > >; - available: number | undefined; - selectAllChunk: (value: boolean) => void; - handleSetAvailable: (value: number | undefined) => void; - createChunk: () => void; - handleInputChange: (e: React.ChangeEvent) => void; - searchString: string; + createChunk: (text: string) => void; } -export default ({ - changeChunkTextMode, - available, - selectAllChunk, - handleSetAvailable, - createChunk, - handleInputChange, - searchString, -}: ChunkResultBarProps) => { +export default ({ changeChunkTextMode, createChunk }: ChunkResultBarProps) => { const { t } = useTranslate('chunk'); const [textSelectValue, setTextSelectValue] = useState( ChunkTextMode.Full, ); - const handleFilterChange = (e: string | number) => { - const value = e === -1 ? undefined : (e as number); - selectAllChunk(false); - handleSetAvailable(value); - }; - const filterContent = ( -
- -
- {t('all')} - {t('enabled')} - {t('disabled')} -
-
-
- ); + // const handleFilterChange = (e: string | number) => { + // const value = e === -1 ? undefined : (e as number); + // selectAllChunk(false); + // handleSetAvailable(value); + // }; + // const filterContent = ( + //
+ // + //
+ // {t('all')} + // {t('enabled')} + // {t('disabled')} + //
+ //
+ //
+ // ); const textSelectOptions = [ { label: t(ChunkTextMode.Full), value: ChunkTextMode.Full }, { label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse }, @@ -78,7 +57,7 @@ export default ({ ))} - {filterContent} - + */} + ); + }, cell: ({ row }) => (
{formatDate(row.original.process_begin_at)} @@ -227,34 +218,60 @@ export const getDatasetLogsTableColumns = ( ), }, { - accessorKey: 'startDate', - header: t('startDate'), + accessorKey: 'process_begin_at', + header: ({ column }) => { + return ( + + ); + }, cell: ({ row }) => ( -
{row.original.startDate}
- ), - }, - { - accessorKey: 'processingType', - header: t('processingType'), - cell: ({ row }) => ( -
- {ProcessingType.knowledgeGraph === row.original.processingType && ( - - )} - {ProcessingType.raptor === row.original.processingType && ( - - )} - {row.original.processingType} +
+ {formatDate(row.original.process_begin_at)}
), }, { - accessorKey: 'status', + accessorKey: 'task_type', + header: t('processingType'), + cell: ({ row }) => ( +
+ {ProcessingType.knowledgeGraph === row.original.task_type && ( + + )} + {ProcessingType.raptor === row.original.task_type && ( + + )} + {ProcessingTypeMap[row.original.task_type as ProcessingType] || + row.original.task_type} +
+ ), + }, + { + accessorKey: 'operation_status', header: t('status'), cell: ({ row }) => ( + // ), }, @@ -294,17 +311,19 @@ const FileLogsTable: FC = ({ const { t } = useTranslate('knowledgeDetails'); const [isModalVisible, setIsModalVisible] = useState(false); const { navigateToDataflowResult } = useNavigatePage(); - const [logInfo, setLogInfo] = useState({}); + const [logInfo, setLogInfo] = useState(); const kowledgeId = useParams().id; const showLog = (row: Row) => { const logDetail = { - taskId: row.original.id, + taskId: row.original?.dsl?.task_id, fileName: row.original.document_name, source: row.original.source_from, - task: row.original.dsl.task_id, + task: row.original?.task_type, status: row.original.statusName, startDate: formatDate(row.original.process_begin_at), - duration: (row.original.process_duration || 0) + 's', + duration: formatSecondsToHumanReadable( + row.original.process_duration || 0, + ), details: row.original.progress_msg, }; console.log('logDetail', logDetail); @@ -331,7 +350,7 @@ const FileLogsTable: FC = ({ [pagination], ); - const table = useReactTable({ + const table = useReactTable({ data: data || [], columns, manualPagination: true, @@ -405,11 +424,14 @@ const FileLogsTable: FC = ({ />
- setIsModalVisible(false)} - logInfo={logInfo} - /> + {isModalVisible && ( + setIsModalVisible(false)} + logInfo={logInfo} + /> + )} ); }; diff --git a/web/src/pages/dataset/dataset/dataset-table.tsx b/web/src/pages/dataset/dataset/dataset-table.tsx index 524a8ba14..cdbeaa3d3 100644 --- a/web/src/pages/dataset/dataset/dataset-table.tsx +++ b/web/src/pages/dataset/dataset/dataset-table.tsx @@ -27,6 +27,7 @@ import { import { UseRowSelectionType } from '@/hooks/logic-hooks/use-row-selection'; import { useFetchDocumentList } from '@/hooks/use-document-request'; import { getExtension } from '@/utils/document-util'; +import { t } from 'i18next'; import { pick } from 'lodash'; import { useMemo } from 'react'; import ProcessLogModal from '../process-log-modal'; @@ -184,6 +185,7 @@ export function DatasetTable({ hideLog()} logInfo={logInfo} diff --git a/web/src/pages/dataset/dataset/generate-button/generate.tsx b/web/src/pages/dataset/dataset/generate-button/generate.tsx index 3b846efed..aa891e215 100644 --- a/web/src/pages/dataset/dataset/generate-button/generate.tsx +++ b/web/src/pages/dataset/dataset/generate-button/generate.tsx @@ -9,63 +9,148 @@ import { import { Modal } from '@/components/ui/modal/modal'; import { cn } from '@/lib/utils'; import { toFixed } from '@/utils/common-util'; +import { UseMutateAsyncFunction } from '@tanstack/react-query'; import { t } from 'i18next'; import { lowerFirst } from 'lodash'; import { CirclePause, Trash2, WandSparkles } from 'lucide-react'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { generateStatus, useFetchGenerateData } from './hook'; +import { replaceText } from '../../process-log-modal'; +import { + ITraceInfo, + generateStatus, + useDatasetGenerate, + useTraceGenerate, +} from './hook'; export enum GenerateType { KnowledgeGraph = 'KnowledgeGraph', Raptor = 'Raptor', } -const MenuItem: React.FC<{ name: GenerateType }> = ({ name }) => { - console.log(name, 'pppp'); +const MenuItem: React.FC<{ + name: GenerateType; + data: ITraceInfo; + pauseGenerate: () => void; + runGenerate: UseMutateAsyncFunction< + any, + Error, + { + type: GenerateType; + }, + unknown + >; +}> = ({ name, runGenerate, data, pauseGenerate }) => { + console.log(name, 'pppp', data); const iconKeyMap = { KnowledgeGraph: 'knowledgegraph', Raptor: 'dataflow-01', }; - const { - data: { percent, type }, - pauseGenerate, - } = useFetchGenerateData(); + const type = useMemo(() => { + if (!data) { + return generateStatus.start; + } + if (data.progress >= 1) { + return generateStatus.completed; + } else if (!data.progress && data.progress !== 0) { + return generateStatus.start; + } else if (data.progress < 0) { + return generateStatus.failed; + } else if (data.progress < 1) { + return generateStatus.running; + } + }, [data]); + + const percent = + type === generateStatus.failed + ? 100 + : type === generateStatus.running + ? data.progress * 100 + : 0; + return ( -
-
- - {t(`knowledgeDetails.${lowerFirst(name)}`)} -
- {type === generateStatus.start && ( -
- {t(`knowledgeDetails.generate${name}`)} -
+ -
-
+ onSelect={(e) => { + e.preventDefault(); + }} + onClick={(e) => { + e.stopPropagation(); + }} + > +
{ + if (type === generateStatus.start) { + runGenerate({ type: name }); + } + }} + > +
+ + {t(`knowledgeDetails.${lowerFirst(name)}`)} +
+ {type === generateStatus.start && ( +
+ {t(`knowledgeDetails.generate${name}`)}
- {toFixed(percent) as string}% - { - pauseGenerate(); - }} - > - - + )} + {(type === generateStatus.running || + type === generateStatus.failed) && ( +
+
+
+
+ {type === generateStatus.running && ( + {(toFixed(percent) as string) + '%'} + )} + { + e.stopPropagation(); + pauseGenerate(); + }} + > + {type === generateStatus.failed ? ( + + ) : ( + + )} + +
+ )} +
+ {replaceText(data?.progress_msg || '')}
- )} -
+
+
); }; const Generate: React.FC = () => { const [open, setOpen] = useState(false); - + const { graphRunData, raptorRunData } = useTraceGenerate({ open }); + const { runGenerate, pauseGenerate } = useDatasetGenerate(); const handleOpenChange = (isOpen: boolean) => { setOpen(isOpen); console.log('Dropdown is now', isOpen ? 'open' : 'closed'); @@ -85,29 +170,30 @@ const Generate: React.FC = () => { {t('knowledgeDetails.generate')} - - { - e.preventDefault(); - }} - onClick={(e) => { - e.stopPropagation(); - }} - > - - - { - e.preventDefault(); - }} - onClick={(e) => { - e.stopPropagation(); - }} - > - - + + {Object.values(GenerateType).map((name) => { + const data = ( + name === GenerateType.KnowledgeGraph + ? graphRunData + : raptorRunData + ) as ITraceInfo; + console.log( + name, + 'data', + data, + !data || (!data.progress && data.progress !== 0), + ); + return ( +
+ +
+ ); + })}
diff --git a/web/src/pages/dataset/dataset/generate-button/hook.ts b/web/src/pages/dataset/dataset/generate-button/hook.ts index 0be24a60d..80f6ce0a9 100644 --- a/web/src/pages/dataset/dataset/generate-button/hook.ts +++ b/web/src/pages/dataset/dataset/generate-button/hook.ts @@ -1,25 +1,128 @@ -import { useQuery } from '@tanstack/react-query'; -import { useCallback } from 'react'; +import message from '@/components/ui/message'; +import kbService from '@/services/knowledge-service'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { t } from 'i18next'; +import { useCallback, useEffect, useState } from 'react'; +import { useParams } from 'umi'; +import { GenerateType } from './generate'; export const generateStatus = { running: 'running', completed: 'completed', start: 'start', + failed: 'failed', }; -const useFetchGenerateData = () => { - let number = 10; - // TODO: 获取数据 - const { data, isFetching: loading } = useQuery({ - queryKey: ['generateData', 'id'], - initialData: { id: 0, percent: 0, type: 'running' }, - gcTime: 0, - refetchInterval: 3000, - queryFn: async () => { - number += Math.random() * 10; - const data = { - id: Math.random(), - percent: number, - type: generateStatus.running, - }; + +enum DatasetKey { + generate = 'generate', +} + +export interface ITraceInfo { + begin_at: string; + chunk_ids: string; + create_date: string; + create_time: number; + digest: string; + doc_id: string; + from_page: number; + id: string; + priority: number; + process_duration: number; + progress: number; + progress_msg: string; + retry_count: number; + task_type: string; + to_page: number; + update_date: string; + update_time: number; +} + +export const useTraceGenerate = ({ open }: { open: boolean }) => { + const { id } = useParams(); + const [isLoopGraphRun, setLoopGraphRun] = useState(false); + const [isLoopRaptorRun, setLoopRaptorRun] = useState(false); + const { data: graphRunData, isFetching: graphRunloading } = + useQuery({ + queryKey: [GenerateType.KnowledgeGraph, id, open], + // initialData: {}, + gcTime: 0, + refetchInterval: isLoopGraphRun ? 5000 : false, + retry: 3, + retryDelay: 1000, + enabled: open, + queryFn: async () => { + const { data } = await kbService.traceGraphRag({ + kb_id: id, + }); + return data?.data || {}; + }, + }); + + const { data: raptorRunData, isFetching: raptorRunloading } = + useQuery({ + queryKey: [GenerateType.Raptor, id, open], + // initialData: {}, + gcTime: 0, + refetchInterval: isLoopRaptorRun ? 5000 : false, + retry: 3, + retryDelay: 1000, + enabled: open, + queryFn: async () => { + const { data } = await kbService.traceRaptor({ + kb_id: id, + }); + return data?.data || {}; + }, + }); + + useEffect(() => { + setLoopGraphRun( + !!( + (graphRunData?.progress || graphRunData?.progress === 0) && + graphRunData?.progress < 1 && + graphRunData?.progress >= 0 + ), + ); + }, [graphRunData?.progress]); + + useEffect(() => { + setLoopRaptorRun( + !!( + (raptorRunData?.progress || raptorRunData?.progress === 0) && + raptorRunData?.progress < 1 && + raptorRunData?.progress >= 0 + ), + ); + }, [raptorRunData?.progress]); + return { + graphRunData, + graphRunloading, + raptorRunData, + raptorRunloading, + }; +}; +export const useDatasetGenerate = () => { + const queryClient = useQueryClient(); + const { id } = useParams(); + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [DatasetKey.generate], + mutationFn: async ({ type }: { type: GenerateType }) => { + const func = + type === GenerateType.KnowledgeGraph + ? kbService.runGraphRag + : kbService.runRaptor; + const { data } = await func({ + kb_id: id, + }); + if (data.code === 0) { + message.success(t('message.operated')); + queryClient.invalidateQueries({ + queryKey: [type], + }); + } return data; }, }); @@ -27,6 +130,5 @@ const useFetchGenerateData = () => { // TODO: pause generate console.log('pause generate'); }, []); - return { data, loading, pauseGenerate }; + return { runGenerate: mutateAsync, pauseGenerate, data, loading }; }; -export { useFetchGenerateData }; diff --git a/web/src/pages/dataset/dataset/hooks.ts b/web/src/pages/dataset/dataset/hooks.ts index 4976dae1b..53ce17fdd 100644 --- a/web/src/pages/dataset/dataset/hooks.ts +++ b/web/src/pages/dataset/dataset/hooks.ts @@ -2,9 +2,12 @@ import { useSetModalState } from '@/hooks/common-hooks'; import { useNextWebCrawl } from '@/hooks/document-hooks'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { IDocumentInfo } from '@/interfaces/database/document'; +import { formatDate, formatSecondsToHumanReadable } from '@/utils/date'; +import { formatBytes } from '@/utils/file-util'; import { useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'umi'; import { ILogInfo } from '../process-log-modal'; +import { RunningStatus } from './constant'; export const useNavigateToOtherPage = () => { const navigate = useNavigate(); @@ -75,15 +78,17 @@ export const useShowLog = (documents: IDocumentInfo[]) => { }; if (findRecord) { log = { - taskId: findRecord.id, - fileName: findRecord.name, - fileSize: findRecord.size + '', - source: findRecord.source_type, - task: findRecord.status, - status: findRecord.run, - startTime: findRecord.process_begin_at, - endTime: findRecord.process_begin_at, - duration: findRecord.process_duration + 's', + fileType: findRecord?.suffix, + uploadedBy: findRecord?.created_by, + fileName: findRecord?.name, + uploadDate: formatDate(findRecord.create_date), + fileSize: formatBytes(findRecord.size || 0), + processBeginAt: formatDate(findRecord.process_begin_at), + chunkNumber: findRecord.chunk_num, + duration: formatSecondsToHumanReadable( + findRecord.process_duration || 0, + ), + status: findRecord.run as RunningStatus, details: findRecord.progress_msg, }; } diff --git a/web/src/pages/dataset/dataset/parsing-card.tsx b/web/src/pages/dataset/dataset/parsing-card.tsx index 17bc857f6..01f07ecd4 100644 --- a/web/src/pages/dataset/dataset/parsing-card.tsx +++ b/web/src/pages/dataset/dataset/parsing-card.tsx @@ -1,9 +1,4 @@ import { Button } from '@/components/ui/button'; -import { - HoverCard, - HoverCardContent, - HoverCardTrigger, -} from '@/components/ui/hover-card'; import { IDocumentInfo } from '@/interfaces/database/document'; import { useTranslation } from 'react-i18next'; import reactStringReplace from 'react-string-replace'; @@ -88,20 +83,13 @@ export const PopoverContent = ({ record }: IProps) => { export function ParsingCard({ record, handleShowLog }: IProps) { return ( - - - - - - - - + ); } diff --git a/web/src/pages/dataset/dataset/parsing-status-cell.tsx b/web/src/pages/dataset/dataset/parsing-status-cell.tsx index 713e6c8b4..1f1703ea1 100644 --- a/web/src/pages/dataset/dataset/parsing-status-cell.tsx +++ b/web/src/pages/dataset/dataset/parsing-status-cell.tsx @@ -46,7 +46,15 @@ export function ParsingStatusCell({ } & UseChangeDocumentParserShowType & UseSaveMetaShowType) { const { t } = useTranslation(); - const { run, parser_id, progress, chunk_num, id } = record; + const { + run, + parser_id, + pipeline_id, + pipeline_name, + progress, + chunk_num, + id, + } = record; const operationIcon = IconMap[run]; const p = Number((progress * 100).toFixed(2)); const { handleRunDocumentByIds } = useHandleRunDocumentByIds(id); @@ -80,7 +88,11 @@ export function ParsingStatusCell({ diff --git a/web/src/pages/dataset/process-log-modal.tsx b/web/src/pages/dataset/process-log-modal.tsx index e43a842c0..96b0c0e12 100644 --- a/web/src/pages/dataset/process-log-modal.tsx +++ b/web/src/pages/dataset/process-log-modal.tsx @@ -3,10 +3,16 @@ import { Button } from '@/components/ui/button'; import { Modal } from '@/components/ui/modal/modal'; import { RunningStatusMap } from '@/constants/knowledge'; import { useTranslate } from '@/hooks/common-hooks'; -import React from 'react'; +import React, { useMemo } from 'react'; import reactStringReplace from 'react-string-replace'; import { RunningStatus } from './dataset/constant'; export interface ILogInfo { + fileType?: string; + uploadedBy?: string; + uploadDate?: string; + processBeginAt?: string; + chunkNumber?: number; + taskId?: string; fileName: string; fileSize?: string; @@ -23,6 +29,7 @@ interface ProcessLogModalProps { visible: boolean; onCancel: () => void; logInfo: ILogInfo; + title: string; } const InfoItem: React.FC<{ @@ -37,35 +44,41 @@ const InfoItem: React.FC<{ ); }; +export const replaceText = (text: string) => { + // Remove duplicate \n + const nextText = text.replace(/(\n)\1+/g, '$1'); + const replacedText = reactStringReplace( + nextText, + /(\[ERROR\].+\s)/g, + (match, i) => { + return ( + + {match} + + ); + }, + ); + + return replacedText; +}; const ProcessLogModal: React.FC = ({ visible, onCancel, - logInfo, + logInfo: initData, + title, }) => { const { t } = useTranslate('knowledgeDetails'); const blackKeyList = ['']; - const replaceText = (text: string) => { - // Remove duplicate \n - const nextText = text.replace(/(\n)\1+/g, '$1'); + console.log('logInfo', initData); + const logInfo = useMemo(() => { + console.log('logInfo', initData); + return initData; + }, [initData]); - const replacedText = reactStringReplace( - nextText, - /(\[ERROR\].+\s)/g, - (match, i) => { - return ( - - {match} - - ); - }, - ); - - return replacedText; - }; return ( = ({ >
- {Object.keys(logInfo).map((key) => { + {Object?.keys(logInfo).map((key) => { if ( blackKeyList.includes(key) || !logInfo[key as keyof typeof logInfo] @@ -86,7 +99,7 @@ const ProcessLogModal: React.FC = ({ } if (key === 'details') { return ( -
+
(methods, request); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index e720a995b..0086fe3fb 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -50,6 +50,10 @@ export default { fetchDataPipelineLog: `${api_host}/kb/list_pipeline_logs`, get_pipeline_detail: `${api_host}/kb/pipeline_log_detail`, fetchPipelineDatasetLogs: `${api_host}/kb/list_pipeline_dataset_logs`, + runGraphRag: `${api_host}/kb/run_graphrag`, + traceGraphRag: `${api_host}/kb/trace_graphrag`, + runRaptor: `${api_host}/kb/run_raptor`, + traceRaptor: `${api_host}/kb/trace_raptor`, // tags listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`, diff --git a/web/src/utils/date.ts b/web/src/utils/date.ts index a9f6da848..1566c8193 100644 --- a/web/src/utils/date.ts +++ b/web/src/utils/date.ts @@ -1,5 +1,4 @@ import dayjs from 'dayjs'; -import { toFixed } from './common-util'; export function formatDate(date: any) { if (!date) { @@ -52,12 +51,13 @@ export function formatSecondsToHumanReadable(seconds: number): string { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); - const s = toFixed(seconds % 60, 3); - + // const s = toFixed(seconds % 60, 3); + const s = seconds % 60; + const formattedSeconds = s === 0 ? '0' : s.toFixed(3).replace(/\.?0+$/, ''); const parts = []; - if (h > 0) parts.push(`${h}h`); - if (m > 0) parts.push(`${m}m`); - if (s || parts.length === 0) parts.push(`${s}s`); + if (h > 0) parts.push(`${h}h `); + if (m > 0) parts.push(`${m}m `); + if (s || parts.length === 0) parts.push(`${formattedSeconds}s`); return parts.join(''); }