Fix: Interface integration for the file log page in the overview #9869 (#10222)

### What problem does this PR solve?

Fix: Interface integration for the file log page in the overview

- Support for selecting data pipeline parsing types
- Use the RunningStatus enumeration instead of numeric status
- Obtain and display data pipeline file log details
- Replace existing mock data with new interface data on the page
- Link the file log list to the real data source
- Optimize log information display
- Fixed a typo in the field name "pipeline_id" → "pipeline_id"

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-09-23 10:33:17 +08:00
committed by GitHub
parent 0c557e37ad
commit f20dca2895
21 changed files with 417 additions and 212 deletions

View File

@ -18,6 +18,10 @@ import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-reques
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IParserConfig } from '@/interfaces/database/document'; import { IParserConfig } from '@/interfaces/database/document';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
import {
ChunkMethodItem,
ParseTypeItem,
} from '@/pages/dataset/dataset-setting/configuration/common-item';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import get from 'lodash/get'; import get from 'lodash/get';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
@ -30,6 +34,7 @@ import {
AutoKeywordsFormField, AutoKeywordsFormField,
AutoQuestionsFormField, AutoQuestionsFormField,
} from '../auto-keywords-form-field'; } from '../auto-keywords-form-field';
import { DataFlowSelect } from '../data-pipeline-select';
import { DelimiterFormField } from '../delimiter-form-field'; import { DelimiterFormField } from '../delimiter-form-field';
import { EntityTypesFormField } from '../entity-types-form-field'; import { EntityTypesFormField } from '../entity-types-form-field';
import { ExcelToHtmlFormField } from '../excel-to-html-form-field'; import { ExcelToHtmlFormField } from '../excel-to-html-form-field';
@ -45,7 +50,6 @@ import RaptorFormFields, {
} from '../parse-configuration/raptor-form-fields'; } from '../parse-configuration/raptor-form-fields';
import { ButtonLoading } from '../ui/button'; import { ButtonLoading } from '../ui/button';
import { Input } from '../ui/input'; import { Input } from '../ui/input';
import { RAGFlowSelect } from '../ui/select';
import { DynamicPageRange } from './dynamic-page-range'; import { DynamicPageRange } from './dynamic-page-range';
import { useFetchParserListOnMount, useShowAutoKeywords } from './hooks'; import { useFetchParserListOnMount, useShowAutoKeywords } from './hooks';
import { import {
@ -62,6 +66,7 @@ interface IProps
}> { }> {
loading: boolean; loading: boolean;
parserId: string; parserId: string;
pipelineId?: string;
parserConfig: IParserConfig; parserConfig: IParserConfig;
documentExtension: string; documentExtension: string;
documentId: string; documentId: string;
@ -80,6 +85,7 @@ export function ChunkMethodDialog({
hideModal, hideModal,
onOk, onOk,
parserId, parserId,
pipelineId,
documentExtension, documentExtension,
visible, visible,
parserConfig, parserConfig,
@ -100,12 +106,14 @@ export function ChunkMethodDialog({
const fillDefaultParserValue = useFillDefaultValueOnMount(); const fillDefaultParserValue = useFillDefaultValueOnMount();
const FormSchema = z.object({ const FormSchema = z.object({
parseType: z.number(),
parser_id: z parser_id: z
.string() .string()
.min(1, { .min(1, {
message: t('common.pleaseSelect'), message: t('common.pleaseSelect'),
}) })
.trim(), .trim(),
pipeline_id: z.string().optional(),
parser_config: z.object({ parser_config: z.object({
task_page_size: z.coerce.number().optional(), task_page_size: z.coerce.number().optional(),
layout_recognize: z.string().optional(), layout_recognize: z.string().optional(),
@ -138,7 +146,8 @@ export function ChunkMethodDialog({
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),
defaultValues: { defaultValues: {
parser_id: parserId, parser_id: parserId,
pipeline_id: pipelineId,
parseType: pipelineId ? 2 : 1,
parser_config: defaultParserValues, parser_config: defaultParserValues,
}, },
}); });
@ -201,6 +210,8 @@ export function ChunkMethodDialog({
parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? []; parserConfig?.pages?.map((x) => ({ from: x[0], to: x[1] })) ?? [];
form.reset({ form.reset({
parser_id: parserId, parser_id: parserId,
pipeline_id: pipelineId,
parseType: pipelineId ? 2 : 1,
parser_config: fillDefaultParserValue({ parser_config: fillDefaultParserValue({
pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }], pages: pages.length > 0 ? pages : [{ from: 1, to: 1024 }],
...omit(parserConfig, 'pages'), ...omit(parserConfig, 'pages'),
@ -223,7 +234,11 @@ export function ChunkMethodDialog({
useGraphRag, useGraphRag,
visible, visible,
]); ]);
const parseType = useWatch({
control: form.control,
name: 'parseType',
defaultValue: 1,
});
return ( return (
<Dialog open onOpenChange={hideModal}> <Dialog open onOpenChange={hideModal}>
<DialogContent className="max-w-[50vw]"> <DialogContent className="max-w-[50vw]">
@ -237,7 +252,17 @@ export function ChunkMethodDialog({
id={FormId} id={FormId}
> >
<FormContainer> <FormContainer>
<FormField <ParseTypeItem />
{parseType === 1 && <ChunkMethodItem></ChunkMethodItem>}
{parseType === 2 && (
<DataFlowSelect
isMult={false}
// toDataPipeline={navigateToAgents}
formFieldName="pipeline_id"
/>
)}
{/* <FormField
control={form.control} control={form.control}
name="parser_id" name="parser_id"
render={({ field }) => ( render={({ field }) => (
@ -252,9 +277,11 @@ export function ChunkMethodDialog({
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> /> */}
{showPages && <DynamicPageRange></DynamicPageRange>} {showPages && parseType === 1 && (
{showPages && layoutRecognize && ( <DynamicPageRange></DynamicPageRange>
)}
{showPages && parseType === 1 && layoutRecognize && (
<FormField <FormField
control={form.control} control={form.control}
name="parser_config.task_page_size" name="parser_config.task_page_size"
@ -279,11 +306,15 @@ export function ChunkMethodDialog({
/> />
)} )}
</FormContainer> </FormContainer>
{parseType === 1 && (
<>
<FormContainer <FormContainer
show={showOne || showMaxTokenNumber} show={showOne || showMaxTokenNumber}
className="space-y-3" className="space-y-3"
> >
{showOne && <LayoutRecognizeFormField></LayoutRecognizeFormField>} {showOne && (
<LayoutRecognizeFormField></LayoutRecognizeFormField>
)}
{showMaxTokenNumber && ( {showMaxTokenNumber && (
<> <>
<MaxTokenNumberFormField <MaxTokenNumberFormField
@ -307,7 +338,9 @@ export function ChunkMethodDialog({
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</> </>
)} )}
{showExcelToHtml && <ExcelToHtmlFormField></ExcelToHtmlFormField>} {showExcelToHtml && (
<ExcelToHtmlFormField></ExcelToHtmlFormField>
)}
</FormContainer> </FormContainer>
{showRaptorParseConfiguration( {showRaptorParseConfiguration(
selectedTag as DocumentParserType, selectedTag as DocumentParserType,
@ -322,7 +355,11 @@ export function ChunkMethodDialog({
<UseGraphRagFormField></UseGraphRagFormField> <UseGraphRagFormField></UseGraphRagFormField>
</FormContainer> </FormContainer>
)} )}
{showEntityTypes && <EntityTypesFormField></EntityTypesFormField>} {showEntityTypes && (
<EntityTypesFormField></EntityTypesFormField>
)}
</>
)}
</form> </form>
</Form> </Form>
<DialogFooter> <DialogFooter>

View File

@ -52,6 +52,7 @@ export function DataFlowSelect(props: IProps) {
> >
{t('dataFlow')} {t('dataFlow')}
</FormLabel> </FormLabel>
{toDataPipeline && (
<div <div
className="text-sm flex text-text-primary cursor-pointer" className="text-sm flex text-text-primary cursor-pointer"
onClick={toDataPipLine} onClick={toDataPipLine}
@ -59,6 +60,7 @@ export function DataFlowSelect(props: IProps) {
{t('buildItFromScratch')} {t('buildItFromScratch')}
<ArrowUpRight size={14} /> <ArrowUpRight size={14} />
</div> </div>
)}
</div> </div>
<div className="text-muted-foreground"> <div className="text-muted-foreground">

View File

@ -1,11 +1,12 @@
// src/pages/dataset/file-logs/file-status-badge.tsx // src/pages/dataset/file-logs/file-status-badge.tsx
import { RunningStatus } from '@/pages/dataset/dataset/constant';
import { FC } from 'react'; import { FC } from 'react';
/** /**
* params: status: 0 not run yet 1 running, 2 cancel, 3 success, 4 fail * params: status: 0 not run yet 1 running, 2 cancel, 3 success, 4 fail
*/ */
interface StatusBadgeProps { interface StatusBadgeProps {
// status: 'Success' | 'Failed' | 'Running' | 'Pending'; // status: 'Success' | 'Failed' | 'Running' | 'Pending';
status: 0 | 1 | 2 | 3 | 4; status: RunningStatus;
name?: string; name?: string;
} }
@ -16,13 +17,13 @@ const FileStatusBadge: FC<StatusBadgeProps> = ({ status, name }) => {
// #00beb4 → rgb(0, 190, 180) // accent-primary // #00beb4 → rgb(0, 190, 180) // accent-primary
// #faad14 → rgb(250, 173, 20) // state-warning // #faad14 → rgb(250, 173, 20) // state-warning
switch (status) { switch (status) {
case 3: case RunningStatus.DONE:
return `bg-[rgba(59,160,92,0.1)] text-state-success`; return `bg-[rgba(59,160,92,0.1)] text-state-success`;
case 4: case RunningStatus.FAIL:
return `bg-[rgba(216,73,75,0.1)] text-state-error`; return `bg-[rgba(216,73,75,0.1)] text-state-error`;
case 1: case RunningStatus.RUNNING:
return `bg-[rgba(0,190,180,0.1)] text-accent-primary`; return `bg-[rgba(0,190,180,0.1)] text-accent-primary`;
case 0: case RunningStatus.UNSTART:
return `bg-[rgba(250,173,20,0.1)] text-state-warning`; return `bg-[rgba(250,173,20,0.1)] text-state-warning`;
default: default:
return 'bg-gray-500/10 text-white'; return 'bg-gray-500/10 text-white';
@ -35,13 +36,13 @@ const FileStatusBadge: FC<StatusBadgeProps> = ({ status, name }) => {
// #00beb4 → rgb(0, 190, 180) // accent-primary // #00beb4 → rgb(0, 190, 180) // accent-primary
// #faad14 → rgb(250, 173, 20) // state-warning // #faad14 → rgb(250, 173, 20) // state-warning
switch (status) { switch (status) {
case 3: case RunningStatus.DONE:
return `bg-[rgba(59,160,92,1)] text-state-success`; return `bg-[rgba(59,160,92,1)] text-state-success`;
case 4: case RunningStatus.FAIL:
return `bg-[rgba(216,73,75,1)] text-state-error`; return `bg-[rgba(216,73,75,1)] text-state-error`;
case 1: case RunningStatus.RUNNING:
return `bg-[rgba(0,190,180,1)] text-accent-primary`; return `bg-[rgba(0,190,180,1)] text-accent-primary`;
case 0: case RunningStatus.UNSTART:
return `bg-[rgba(250,173,20,1)] text-state-warning`; return `bg-[rgba(250,173,20,1)] text-state-warning`;
default: default:
return 'bg-gray-500/10 text-white'; return 'bg-gray-500/10 text-white';

View File

@ -15,6 +15,14 @@ export enum RunningStatus {
FAIL = '4', // need to refresh FAIL = '4', // need to refresh
} }
export const RunningStatusMap = {
[RunningStatus.UNSTART]: 'Pending',
[RunningStatus.RUNNING]: 'Running',
[RunningStatus.CANCEL]: 'Cancel',
[RunningStatus.DONE]: 'Success',
[RunningStatus.FAIL]: 'Failed',
};
export enum ModelVariableType { export enum ModelVariableType {
Improvise = 'Improvise', Improvise = 'Improvise',
Precise = 'Precise', Precise = 'Precise',

View File

@ -93,8 +93,8 @@ export const useNavigatePage = () => {
const navigateToChunkParsedResult = useCallback( const navigateToChunkParsedResult = useCallback(
(id: string, knowledgeId?: string) => () => { (id: string, knowledgeId?: string) => () => {
navigate( navigate(
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`, `${Routes.ParsedResult}/chunks?id=${knowledgeId}&doc_id=${id}`,
`${Routes.DataflowResult}?id=${knowledgeId}&doc_id=${id}&type=chunk`, // `${Routes.DataflowResult}?id=${knowledgeId}&doc_id=${id}&type=chunk`,
); );
}, },
[navigate], [navigate],
@ -136,7 +136,7 @@ export const useNavigatePage = () => {
(id: string, knowledgeId?: string) => () => { (id: string, knowledgeId?: string) => () => {
navigate( navigate(
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`, // `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`,
`${Routes.DataflowResult}?id=${knowledgeId}&doc_id=${id}&type=dataflow`, `${Routes.DataflowResult}?id=${id}&type=dataflow`,
); );
}, },
[navigate], [navigate],

View File

@ -335,15 +335,18 @@ export const useSetDocumentParser = () => {
mutationKey: [DocumentApiAction.SetDocumentParser], mutationKey: [DocumentApiAction.SetDocumentParser],
mutationFn: async ({ mutationFn: async ({
parserId, parserId,
pipelineId,
documentId, documentId,
parserConfig, parserConfig,
}: { }: {
parserId: string; parserId: string;
pipelineId: string;
documentId: string; documentId: string;
parserConfig: IChangeParserConfigRequestBody; parserConfig: IChangeParserConfigRequestBody;
}) => { }) => {
const { data } = await kbService.document_change_parser({ const { data } = await kbService.document_change_parser({
parser_id: parserId, parser_id: parserId,
pipeline_id: pipelineId,
doc_id: documentId, doc_id: documentId,
parser_config: parserConfig, parser_config: parserConfig,
}); });

View File

@ -7,6 +7,7 @@ export interface IChangeParserConfigRequestBody {
export interface IChangeParserRequestBody { export interface IChangeParserRequestBody {
parser_id: string; parser_id: string;
pipeline_id: string;
doc_id: string; doc_id: string;
parser_config: IChangeParserConfigRequestBody; parser_config: IChangeParserConfigRequestBody;
} }

View File

@ -114,6 +114,9 @@ export default {
processingType: 'Processing Type', processingType: 'Processing Type',
dataPipeline: 'Data Pipeline', dataPipeline: 'Data Pipeline',
operations: 'Operations', operations: 'Operations',
taskId: 'Task ID',
duration: 'Duration',
details: 'Details',
status: 'Status', status: 'Status',
task: 'Task', task: 'Task',
startDate: 'Start Date', startDate: 'Start Date',

View File

@ -102,6 +102,9 @@ export default {
processingType: '处理类型', processingType: '处理类型',
dataPipeline: '数据管道', dataPipeline: '数据管道',
operations: '操作', operations: '操作',
taskId: '任务ID',
duration: '耗时',
details: '详情',
status: '状态', status: '状态',
task: '任务', task: '任务',
startDate: '开始时间', startDate: '开始时间',

View File

@ -7,13 +7,74 @@ import {
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks'; import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import kbService from '@/services/knowledge-service';
import { buildChunkHighlights } from '@/utils/document-util'; import { buildChunkHighlights } from '@/utils/document-util';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { IHighlight } from 'react-pdf-highlighter'; import { IHighlight } from 'react-pdf-highlighter';
import { useParams, useSearchParams } from 'umi';
import { ChunkTextMode } from './constant'; import { ChunkTextMode } from './constant';
export interface IPipelineFileLogDetail {
avatar: string;
create_date: string;
create_time: number;
document_id: string;
document_name: string;
document_suffix: string;
document_type: string;
dsl: string;
id: string;
kb_id: string;
operation_status: string;
parser_id: string;
pipeline_id: string;
pipeline_title: string;
process_begin_at: string;
process_duration: number;
progress: number;
progress_msg: string;
source_from: string;
status: string;
task_type: string;
tenant_id: string;
update_date: string;
update_time: number;
}
export const useFetchPipelineFileLogDetail = (props?: {
isEdit?: boolean;
refreshCount?: number;
}) => {
const { isEdit = true, refreshCount } = props || { isEdit: true };
const { id } = useParams();
const [searchParams] = useSearchParams();
const logId = searchParams.get('id') || id;
let queryKey: (string | number)[] = [];
if (typeof refreshCount === 'number') {
queryKey = ['fetchLogDetail', refreshCount];
}
const { data, isFetching: loading } = useQuery<IPipelineFileLogDetail>({
queryKey,
initialData: {} as IPipelineFileLogDetail,
gcTime: 0,
queryFn: async () => {
if (isEdit) {
const { data } = await kbService.get_pipeline_detail({
log_id: logId,
});
return data?.data ?? {};
} else {
return {};
}
},
});
return { data, loading };
};
export const useHandleChunkCardClick = () => { export const useHandleChunkCardClick = () => {
const [selectedChunkId, setSelectedChunkId] = useState<string>(''); const [selectedChunkId, setSelectedChunkId] = useState<string>('');

View File

@ -3,6 +3,7 @@ import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import DocumentPreview from './components/document-preview'; import DocumentPreview from './components/document-preview';
import { import {
useFetchPipelineFileLogDetail,
useGetChunkHighlights, useGetChunkHighlights,
useHandleChunkCardClick, useHandleChunkCardClick,
useRerunDataflow, useRerunDataflow,
@ -28,7 +29,6 @@ import {
useNavigatePage, useNavigatePage,
} from '@/hooks/logic-hooks/navigate-hooks'; } from '@/hooks/logic-hooks/navigate-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { ChunkerContainer } from './chunker'; import { ChunkerContainer } from './chunker';
import { useGetDocumentUrl } from './components/document-preview/hooks'; import { useGetDocumentUrl } from './components/document-preview/hooks';
import TimelineDataFlow, { import TimelineDataFlow, {
@ -44,7 +44,7 @@ const Chunk = () => {
} = useFetchNextChunkList(); } = useFetchNextChunkList();
const { selectedChunkId } = useHandleChunkCardClick(); const { selectedChunkId } = useHandleChunkCardClick();
const [activeStepId, setActiveStepId] = useState<number | string>(0); const [activeStepId, setActiveStepId] = useState<number | string>(0);
const { data: dataset } = useFetchKnowledgeBaseConfiguration(); const { data: dataset } = useFetchPipelineFileLogDetail();
const { isChange, setIsChange } = useRerunDataflow(); const { isChange, setIsChange } = useRerunDataflow();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,5 +1,12 @@
import kbService from '@/services/knowledge-service'; import {
useGetPaginationWithRouter,
useHandleSearchChange,
} from '@/hooks/logic-hooks';
import kbService, {
listDataPipelineLogDocument,
} from '@/services/knowledge-service';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useCallback } from 'react';
import { useParams, useSearchParams } from 'umi'; import { useParams, useSearchParams } from 'umi';
export interface IOverviewTital { export interface IOverviewTital {
@ -24,4 +31,78 @@ const useFetchOverviewTital = () => {
return { data }; return { data };
}; };
export { useFetchOverviewTital }; export interface IFileLogItem {
create_date: string;
create_time: number;
document_id: string;
document_name: string;
document_suffix: string;
document_type: string;
dsl: any;
path: string[];
task_id: string;
id: string;
name: string;
kb_id: string;
operation_status: string;
parser_id: string;
pipeline_id: string;
pipeline_title: string;
avatar: string;
process_begin_at: null | string;
process_duration: number;
progress: number;
progress_msg: string;
source_from: string;
status: string;
task_type: string;
tenant_id: string;
update_date: string;
update_time: number;
}
export interface IFileLogList {
docs: IFileLogItem[];
total: number;
}
const useFetchFileLogList = () => {
const [searchParams] = useSearchParams();
const { searchString, handleInputChange } = useHandleSearchChange();
const { pagination, setPagination } = useGetPaginationWithRouter();
const { id } = useParams();
const knowledgeBaseId = searchParams.get('id') || id;
const { data } = useQuery<IFileLogList>({
queryKey: [
'fileLogList',
knowledgeBaseId,
pagination.current,
pagination.pageSize,
searchString,
],
queryFn: async () => {
const { data: res = {} } = await listDataPipelineLogDocument({
kb_id: knowledgeBaseId,
page: pagination.current,
page_size: pagination.pageSize,
keywords: searchString,
// order_by: '',
});
return res.data || [];
},
});
const onInputChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
(e) => {
setPagination({ page: 1 });
handleInputChange(e);
},
[handleInputChange, setPagination],
);
return {
data,
searchString,
handleInputChange: onInputChange,
pagination: { ...pagination, total: data?.total },
};
};
export { useFetchFileLogList, useFetchOverviewTital };

View File

@ -6,7 +6,7 @@ import { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { LogTabs } from './dataset-common'; import { LogTabs } from './dataset-common';
import { DatasetFilter } from './dataset-filter'; import { DatasetFilter } from './dataset-filter';
import { useFetchOverviewTital } from './hook'; import { useFetchFileLogList, useFetchOverviewTital } from './hook';
import FileLogsTable from './overview-table'; import FileLogsTable from './overview-table';
interface StatCardProps { interface StatCardProps {
@ -94,68 +94,37 @@ const FileLogsPage: FC = () => {
const { data: topData } = useFetchOverviewTital(); const { data: topData } = useFetchOverviewTital();
console.log('topData --> ', topData); console.log('topData --> ', topData);
useEffect(() => { useEffect(() => {
setTopAllData({ setTopAllData((prev) => {
...topAllData, return {
...prev,
processing: { processing: {
value: topData?.processing || 0, value: topData?.processing || 0,
success: topData?.finished || 0, success: topData?.finished || 0,
failed: topData?.failed || 0, failed: topData?.failed || 0,
}, },
});
}, [topData, topAllData]);
const mockData = useMemo(() => {
if (active === LogTabs.FILE_LOGS) {
return Array(30)
.fill(0)
.map((_, i) => ({
id: i === 0 ? '952734' : `14`,
fileName: 'PRD for DealBees 1.2 (1).txt',
source: 'GitHub',
pipeline:
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kikis demo',
startDate: '14/03/2025 14:53:39',
task: i === 0 ? 'chunck' : 'Parser',
status: i === 0 ? 3 : i === 1 ? 4 : i === 2 ? 1 : 0,
statusName:
i === 0
? 'Success'
: i === 1
? 'Failed'
: i === 2
? 'Running'
: 'Pending',
}));
}
if (active === LogTabs.DATASET_LOGS) {
return Array(8)
.fill(0)
.map((_, i) => ({
id: i === 0 ? '952734' : `14`,
fileName: 'PRD for DealBees 1.2 (1).txt',
source: 'GitHub',
startDate: '14/03/2025 14:53:39',
task: i === 0 ? 'chunck' : 'Parser',
pipeline:
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kikis demo',
status: i === 0 ? 3 : i === 1 ? 4 : i === 2 ? 1 : 0,
statusName:
i === 0
? 'Success'
: i === 1
? 'Failed'
: i === 2
? 'Running'
: 'Pending',
}));
}
}, [active]);
const pagination = {
current: 1,
pageSize: 10,
total: 100,
}; };
});
}, [topData]);
const {
data: tableOriginData,
searchString,
handleInputChange,
pagination,
} = useFetchFileLogList();
const tableList = useMemo(() => {
console.log('tableList', tableOriginData);
if (tableOriginData && tableOriginData.docs?.length) {
return tableOriginData.docs.map((item) => {
return {
...item,
fileName: item.document_name,
statusName: item.operation_status,
};
});
}
}, [tableOriginData, active]);
const changeActiveLogs = (active: (typeof LogTabs)[keyof typeof LogTabs]) => { const changeActiveLogs = (active: (typeof LogTabs)[keyof typeof LogTabs]) => {
setActive(active); setActive(active);
@ -223,11 +192,16 @@ const FileLogsPage: FC = () => {
</div> </div>
{/* Tabs & Search */} {/* Tabs & Search */}
<DatasetFilter active={active} setActive={changeActiveLogs} /> <DatasetFilter
active={active}
setActive={changeActiveLogs}
searchString={searchString}
onSearchChange={handleInputChange}
/>
{/* Table */} {/* Table */}
<FileLogsTable <FileLogsTable
data={mockData} data={tableList}
pagination={pagination} pagination={pagination}
setPagination={handlePaginationChange} setPagination={handlePaginationChange}
pageCount={10} pageCount={10}

View File

@ -12,8 +12,10 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from '@/components/ui/table'; } from '@/components/ui/table';
import { RunningStatusMap } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { formatDate } from '@/utils/date';
import { import {
ColumnDef, ColumnDef,
ColumnFiltersState, ColumnFiltersState,
@ -28,18 +30,16 @@ import {
} from '@tanstack/react-table'; } from '@tanstack/react-table';
import { TFunction } from 'i18next'; import { TFunction } from 'i18next';
import { ClipboardList, Eye } from 'lucide-react'; import { ClipboardList, Eye } from 'lucide-react';
import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react'; import { FC, useMemo, useState } from 'react';
import { RunningStatus } from '../dataset/constant';
import ProcessLogModal from '../process-log-modal'; import ProcessLogModal from '../process-log-modal';
import { LogTabs, ProcessingType } from './dataset-common'; import { LogTabs, ProcessingType } from './dataset-common';
import { IFileLogItem } from './hook';
interface DocumentLog { interface DocumentLog {
id: string;
fileName: string; fileName: string;
source: string; status: RunningStatus;
pipeline: string; statusName: typeof RunningStatusMap;
startDate: string;
task: string;
status: 'Success' | 'Failed' | 'Running' | 'Pending';
} }
interface FileLogsTableProps { interface FileLogsTableProps {
@ -57,14 +57,14 @@ interface FileLogsTableProps {
export const getFileLogsTableColumns = ( export const getFileLogsTableColumns = (
t: TFunction<'translation', string>, t: TFunction<'translation', string>,
setIsModalVisible: Dispatch<SetStateAction<boolean>>, showLog: (row: Row<IFileLogItem & DocumentLog>, active: LogTabs) => void,
navigateToDataflowResult: ( navigateToDataflowResult: (
id: string, id: string,
knowledgeId?: string | undefined, knowledgeId?: string | undefined,
) => () => void, ) => () => void,
) => { ) => {
// const { t } = useTranslate('knowledgeDetails'); // const { t } = useTranslate('knowledgeDetails');
const columns: ColumnDef<DocumentLog>[] = [ const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [
// { // {
// id: 'select', // id: 'select',
// header: ({ table }) => ( // header: ({ table }) => (
@ -97,10 +97,10 @@ export const getFileLogsTableColumns = (
cell: ({ row }) => ( cell: ({ row }) => (
<div <div
className="flex items-center gap-2 text-text-primary" className="flex items-center gap-2 text-text-primary"
onClick={navigateToDataflowResult( // onClick={navigateToDataflowResult(
row.original.id, // row.original.id,
row.original.kb_id, // row.original.kb_id,
)} // )}
> >
<FileIcon name={row.original.fileName}></FileIcon> <FileIcon name={row.original.fileName}></FileIcon>
{row.original.fileName} {row.original.fileName}
@ -108,47 +108,51 @@ export const getFileLogsTableColumns = (
), ),
}, },
{ {
accessorKey: 'source', accessorKey: 'source_from',
header: t('source'), header: t('source'),
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-text-primary">{row.original.source}</div> <div className="text-text-primary">{row.original.source_from}</div>
), ),
}, },
{ {
accessorKey: 'pipeline', accessorKey: 'pipeline_title',
header: t('dataPipeline'), header: t('dataPipeline'),
cell: ({ row }) => ( cell: ({ row }) => (
<div className="flex items-center gap-2 text-text-primary"> <div className="flex items-center gap-2 text-text-primary">
<RAGFlowAvatar <RAGFlowAvatar
avatar={null} avatar={row.original.avatar}
name={row.original.pipeline} name={row.original.pipeline_title}
className="size-4" className="size-4"
/> />
{row.original.pipeline} {row.original.pipeline_title}
</div> </div>
), ),
}, },
{ {
accessorKey: 'startDate', accessorKey: 'process_begin_at',
header: t('startDate'), header: t('startDate'),
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-text-primary">{row.original.startDate}</div> <div className="text-text-primary">
{formatDate(row.original.process_begin_at)}
</div>
), ),
}, },
{ {
accessorKey: 'task', accessorKey: 'task_type',
header: t('task'), header: t('task'),
cell: ({ row }) => ( cell: ({ row }) => (
<div className="text-text-primary">{row.original.task}</div> <div className="text-text-primary">{row.original.task_type}</div>
), ),
}, },
{ {
accessorKey: 'status', accessorKey: 'operation_status',
header: t('status'), header: t('status'),
cell: ({ row }) => ( cell: ({ row }) => (
<FileStatusBadge <FileStatusBadge
status={row.original.status} status={row.original.operation_status as RunningStatus}
name={row.original.statusName} name={
RunningStatusMap[row.original.operation_status as RunningStatus]
}
/> />
), ),
}, },
@ -162,7 +166,7 @@ export const getFileLogsTableColumns = (
size="sm" size="sm"
className="p-1" className="p-1"
onClick={() => { onClick={() => {
setIsModalVisible(true); showLog(row, LogTabs.FILE_LOGS);
}} }}
> >
<Eye /> <Eye />
@ -185,10 +189,10 @@ export const getFileLogsTableColumns = (
export const getDatasetLogsTableColumns = ( export const getDatasetLogsTableColumns = (
t: TFunction<'translation', string>, t: TFunction<'translation', string>,
showLog: (row: Row<DocumentLog>, active: LogTabs) => void, showLog: (row: Row<IFileLogItem & DocumentLog>, active: LogTabs) => void,
) => { ) => {
// const { t } = useTranslate('knowledgeDetails'); // const { t } = useTranslate('knowledgeDetails');
const columns: ColumnDef<DocumentLog>[] = [ const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [
// { // {
// id: 'select', // id: 'select',
// header: ({ table }) => ( // header: ({ table }) => (
@ -270,18 +274,6 @@ export const getDatasetLogsTableColumns = (
return columns; return columns;
}; };
const taskInfo = {
taskId: '#9527',
fileName: 'PRD for DealBees 1.2 (1).text',
fileSize: '2.4G',
source: 'Github',
task: 'Parse',
state: 'Running',
startTime: '14/03/2025 14:53:39',
duration: '800',
details:
'\n17:43:21 Task has been received.\n17:43:25 Page(1~100000001): Start to parse.\n17:43:25 Page(1~100000001): Start to tag for every chunk ...\n17:43:45 Page(1~100000001): Tagging 2 chunks completed in 18.99s\n17:43:45 Page(1~100000001): Generate 2 chunks\n17:43:55 Page(1~100000001): Embedding chunks (10.60s)\n17:43:55 Page(1~100000001): Indexing done (0.07s). Task done (33.97s)\n17:43:58 created task raptor\n17:43:58 Task has been received.\n17:44:36 Cluster one layer: 2 -> 1\n17:44:36 Indexing done (0.05s). Task done (37.88s)\n17:44:40 created task graphrag\n17:44:41 Task has been received.\n17:50:57 Entities extraction of chunk 0 1/3 done, 25 nodes, 26 edges, 14893 tokens.\n17:56:01 [ERROR][Exception]: Operation timed out after 7200 seconds and 1 attempts.',
};
const FileLogsTable: FC<FileLogsTableProps> = ({ const FileLogsTable: FC<FileLogsTableProps> = ({
data, data,
pagination, pagination,
@ -295,15 +287,26 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const { navigateToDataflowResult } = useNavigatePage(); const { navigateToDataflowResult } = useNavigatePage();
const [logInfo, setLogInfo] = useState(taskInfo); const [logInfo, setLogInfo] = useState<IFileLogItem>({});
const showLog = (row: Row<DocumentLog>, active: LogTabs) => { const showLog = (row: Row<IFileLogItem & DocumentLog>, active: LogTabs) => {
setLogInfo(row.original); const logDetail = {
taskId: row.original.id,
fileName: row.original.document_name,
source: row.original.source_from,
task: row.original.dsl.task_id,
status: row.original.statusName,
startDate: formatDate(row.original.process_begin_at),
duration: (row.original.process_duration || 0) + 's',
details: row.original.progress_msg,
};
console.log('logDetail', logDetail);
setLogInfo(logDetail);
setIsModalVisible(true); setIsModalVisible(true);
}; };
const columns = useMemo(() => { const columns = useMemo(() => {
return active === LogTabs.FILE_LOGS return active === LogTabs.FILE_LOGS
? getFileLogsTableColumns(t, setIsModalVisible, navigateToDataflowResult) ? getFileLogsTableColumns(t, showLog, navigateToDataflowResult)
: getDatasetLogsTableColumns(t, showLog); : getDatasetLogsTableColumns(t, showLog);
}, [active, t]); }, [active, t]);
@ -316,7 +319,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
); );
const table = useReactTable({ const table = useReactTable({
data, data: data || [],
columns, columns,
manualPagination: true, manualPagination: true,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
@ -354,7 +357,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
))} ))}
</TableHeader> </TableHeader>
<TableBody className="relative"> <TableBody className="relative">
{table.getRowModel().rows.length ? ( {table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => ( table.getRowModel().rows.map((row) => (
<TableRow <TableRow
key={row.id} key={row.id}

View File

@ -11,7 +11,7 @@ export const RunningStatusMap = {
}, },
[RunningStatus.CANCEL]: { label: 'CANCEL', color: 'var(--state-warning)' }, [RunningStatus.CANCEL]: { label: 'CANCEL', color: 'var(--state-warning)' },
[RunningStatus.DONE]: { label: 'SUCCESS', color: 'var(--state-success)' }, [RunningStatus.DONE]: { label: 'SUCCESS', color: 'var(--state-success)' },
[RunningStatus.FAIL]: { label: 'FAIL', color: 'var(--state-error' }, [RunningStatus.FAIL]: { label: 'FAIL', color: 'rgba(var(--state-error))' },
}; };
export * from '@/constants/knowledge'; export * from '@/constants/knowledge';

View File

@ -19,6 +19,7 @@ export const useChangeDocumentParser = () => {
if (record?.id) { if (record?.id) {
const ret = await setDocumentParser({ const ret = await setDocumentParser({
parserId: parserConfigInfo.parser_id, parserId: parserConfigInfo.parser_id,
pipelineId: parserConfigInfo.pipeline_id,
documentId: record?.id, documentId: record?.id,
parserConfig: parserConfigInfo.parser_config, parserConfig: parserConfigInfo.parser_config,
}); });

View File

@ -1,16 +1,18 @@
import FileStatusBadge from '@/components/file-status-badge'; import FileStatusBadge from '@/components/file-status-badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Modal } from '@/components/ui/modal/modal'; import { Modal } from '@/components/ui/modal/modal';
import { RunningStatusMap } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import React from 'react'; import React from 'react';
import reactStringReplace from 'react-string-replace'; import reactStringReplace from 'react-string-replace';
import { RunningStatus } from './dataset/constant';
export interface ILogInfo { export interface ILogInfo {
taskId?: string; taskId?: string;
fileName: string; fileName: string;
fileSize?: string; fileSize?: string;
source?: string; source?: string;
task?: string; task?: string;
state?: 'Running' | 'Success' | 'Failed' | 'Pending'; status?: RunningStatus;
startTime?: string; startTime?: string;
endTime?: string; endTime?: string;
duration?: string; duration?: string;
@ -76,7 +78,10 @@ const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
<div className=" rounded-lg"> <div className=" rounded-lg">
<div className="flex flex-wrap "> <div className="flex flex-wrap ">
{Object.keys(logInfo).map((key) => { {Object.keys(logInfo).map((key) => {
if (blackKeyList.includes(key)) { if (
blackKeyList.includes(key) ||
!logInfo[key as keyof typeof logInfo]
) {
return null; return null;
} }
if (key === 'details') { if (key === 'details') {
@ -93,12 +98,17 @@ const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
</div> </div>
); );
} }
if (key === 'Status') { if (key === 'status') {
return ( return (
<div className="flex flex-col" key={key}> <div className="flex flex-col w-1/2" key={key}>
<span className="text-text-secondary text-sm">Status</span> <span className="text-text-secondary text-sm">
{t('status')}
</span>
<div className="mt-1"> <div className="mt-1">
<FileStatusBadge status={logInfo.state} /> <FileStatusBadge
status={logInfo.status as RunningStatus}
name={RunningStatusMap[logInfo.status as RunningStatus]}
/>
</div> </div>
</div> </div>
); );

View File

@ -9,7 +9,13 @@ import { cn, formatBytes } from '@/lib/utils';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { formatPureDate } from '@/utils/date'; import { formatPureDate } from '@/utils/date';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { Banknote, FileSearch2, FolderOpen, GitGraph } from 'lucide-react'; import {
Banknote,
DatabaseZap,
FileSearch2,
FolderOpen,
GitGraph,
} from 'lucide-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHandleMenuClick } from './hooks'; import { useHandleMenuClick } from './hooks';
@ -28,11 +34,11 @@ export function SideBar({ refreshCount }: PropType) {
const items = useMemo(() => { const items = useMemo(() => {
const list = [ const list = [
// { {
// icon: DatabaseZap, icon: DatabaseZap,
// label: t(`knowledgeDetails.overview`), label: t(`knowledgeDetails.overview`),
// key: Routes.DataSetOverview, key: Routes.DataSetOverview,
// }, },
{ {
icon: FolderOpen, icon: FolderOpen,
label: t(`knowledgeDetails.subbarFiles`), label: t(`knowledgeDetails.subbarFiles`),

View File

@ -19,7 +19,7 @@ import { Input } from '@/components/ui/input';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
@ -50,7 +50,7 @@ export function InputForm({ onOk }: IModalProps<any>) {
}) })
.trim(), .trim(),
parser_id: z.string().optional(), parser_id: z.string().optional(),
pipline_id: z.string().optional(), pipeline_id: z.string().optional(),
}) })
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
// When parseType === 1, parser_id is required // When parseType === 1, parser_id is required
@ -67,11 +67,11 @@ export function InputForm({ onOk }: IModalProps<any>) {
console.log('form-data', data); console.log('form-data', data);
// When parseType === 1, pipline_id required // When parseType === 1, pipline_id required
if (data.parseType === 2 && !data.pipline_id) { if (data.parseType === 2 && !data.pipeline_id) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: t('knowledgeList.dataFlowRequired'), message: t('knowledgeList.dataFlowRequired'),
path: ['pipline_id'], path: ['pipeline_id'],
}); });
} }
}); });
@ -126,19 +126,13 @@ export function InputForm({ onOk }: IModalProps<any>) {
<EmbeddingModelItem line={2} isEdit={false} /> <EmbeddingModelItem line={2} isEdit={false} />
<ParseTypeItem /> <ParseTypeItem />
{parseType === 1 && ( {parseType === 1 && <ChunkMethodItem></ChunkMethodItem>}
<>
<ChunkMethodItem></ChunkMethodItem>
</>
)}
{parseType === 2 && ( {parseType === 2 && (
<>
<DataFlowSelect <DataFlowSelect
isMult={false} isMult={false}
toDataPipeline={navigateToAgents} toDataPipeline={navigateToAgents}
formFieldName="pipline_id" formFieldName="pipeline_id"
/> />
</>
)} )}
</form> </form>
</Form> </Form>

View File

@ -40,6 +40,7 @@ const {
getMeta, getMeta,
retrievalTestShare, retrievalTestShare,
getKnowledgeBasicInfo, getKnowledgeBasicInfo,
fetchDataPipelineLog,
} = api; } = api;
const methods = { const methods = {
@ -174,6 +175,14 @@ const methods = {
url: getKnowledgeBasicInfo, url: getKnowledgeBasicInfo,
method: 'get', method: 'get',
}, },
fetchDataPipelineLog: {
url: fetchDataPipelineLog,
method: 'post',
},
get_pipeline_detail: {
url: api.get_pipeline_detail,
method: 'get',
},
}; };
const kbService = registerServer<keyof typeof methods>(methods, request); const kbService = registerServer<keyof typeof methods>(methods, request);
@ -210,4 +219,9 @@ export const listDocument = (
export const documentFilter = (kb_id: string) => export const documentFilter = (kb_id: string) =>
request.post(api.get_dataset_filter, { kb_id }); request.post(api.get_dataset_filter, { kb_id });
export const listDataPipelineLogDocument = (
params?: IFetchKnowledgeListRequestParams,
body?: IFetchDocumentListRequestBody,
) => request.post(api.fetchDataPipelineLog, { data: body || {}, params });
export default kbService; export default kbService;

View File

@ -46,6 +46,9 @@ export default {
`${api_host}/kb/${knowledgeId}/knowledge_graph`, `${api_host}/kb/${knowledgeId}/knowledge_graph`,
getMeta: `${api_host}/kb/get_meta`, getMeta: `${api_host}/kb/get_meta`,
getKnowledgeBasicInfo: `${api_host}/kb/basic_info`, getKnowledgeBasicInfo: `${api_host}/kb/basic_info`,
// data pipeline log
fetchDataPipelineLog: `${api_host}/kb/list_pipeline_logs`,
get_pipeline_detail: `${api_host}/kb/pipeline_log_detail`,
// tags // tags
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`, listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,