mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-25 16:26:51 +08:00
### What problem does this PR solve? Feat: Cancel a running data flow test #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -7,7 +7,10 @@ import {
|
||||
TimelineSeparator,
|
||||
TimelineTitle,
|
||||
} from '@/components/originui/timeline';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { File } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { Operator } from '../constant';
|
||||
import OperatorIcon from '../operator-icon';
|
||||
@ -17,6 +20,8 @@ export type DataflowTimelineProps = {
|
||||
traceList?: ITraceData[];
|
||||
};
|
||||
|
||||
const END = 'END';
|
||||
|
||||
interface DataflowTrace {
|
||||
datetime: string;
|
||||
elapsed_time: number;
|
||||
@ -48,43 +53,66 @@ export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
|
||||
const traces = item.trace as DataflowTrace[];
|
||||
const nodeLabel = getNodeLabel(item.component_id);
|
||||
|
||||
const latest = traces[traces.length - 1];
|
||||
const progress = latest.progress * 100;
|
||||
|
||||
return (
|
||||
<TimelineItem
|
||||
key={item.component_id}
|
||||
step={index}
|
||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
|
||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8 pb-6"
|
||||
>
|
||||
<TimelineHeader>
|
||||
<TimelineSeparator className="group-data-[orientation=vertical]/timeline:-left-7 group-data-[orientation=vertical]/timeline:h-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=vertical]/timeline:translate-y-7 bg-accent-primary" />
|
||||
<TimelineTitle className="">
|
||||
<TimelineContent className="text-foreground mt-2 rounded-lg border px-4 py-3">
|
||||
<p className="mb-2">
|
||||
{getNodeData(item.component_id)?.name || 'END'}
|
||||
</p>
|
||||
<TimelineContent
|
||||
className={cn(
|
||||
'text-foreground rounded-lg border px-4 py-3',
|
||||
)}
|
||||
>
|
||||
<section className="flex items-center justify-between mb-2">
|
||||
<span className="flex-1 truncate">
|
||||
{getNodeData(item.component_id)?.name || END}
|
||||
</span>
|
||||
<div className="flex-1 flex items-center gap-5">
|
||||
<Progress value={progress} className="h-1 flex-1" />
|
||||
<span className="text-accent-primary text-xs">
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<div className="divide-y space-y-1">
|
||||
{traces.map((x, idx) => (
|
||||
<section
|
||||
key={idx}
|
||||
className="text-text-secondary text-xs"
|
||||
className="text-text-secondary text-xs space-x-2 py-2.5 !m-0"
|
||||
>
|
||||
<div className="space-x-2">
|
||||
<span>{x.datetime}</span>
|
||||
<span>{x.progress * 100}%</span>
|
||||
<span>{x.elapsed_time.toString().slice(0, 6)}</span>
|
||||
</div>
|
||||
<span>{x.datetime}</span>
|
||||
{item.component_id !== 'END' && (
|
||||
<div>{x.message}</div>
|
||||
<span>{x.message}</span>
|
||||
)}
|
||||
<span>{x.elapsed_time.toString().slice(0, 6)}</span>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</TimelineContent>
|
||||
</TimelineTitle>
|
||||
<TimelineIndicator className="border border-accent-primary group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7">
|
||||
{nodeLabel && (
|
||||
<TimelineIndicator
|
||||
className={cn(
|
||||
'border border-accent-primary group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-5 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7',
|
||||
{
|
||||
'rounded bg-accent-primary': nodeLabel === Operator.Begin,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{item.component_id === END ? (
|
||||
<span className="rounded-full inline-block size-2 bg-accent-primary"></span>
|
||||
) : nodeLabel === Operator.Begin ? (
|
||||
<File className="size-3.5 text-bg-base"></File>
|
||||
) : (
|
||||
<OperatorIcon
|
||||
name={nodeLabel}
|
||||
className="size-6 rounded-full"
|
||||
className="size-3.5 rounded-full"
|
||||
></OperatorIcon>
|
||||
)}
|
||||
</TimelineIndicator>
|
||||
|
||||
@ -5,11 +5,15 @@ import {
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { NotebookText, SquareArrowOutUpRight } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
CirclePause,
|
||||
Logs,
|
||||
SquareArrowOutUpRight,
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import 'react18-json-view/src/style.css';
|
||||
import {
|
||||
@ -18,39 +22,53 @@ import {
|
||||
} from '../hooks/use-download-output';
|
||||
import { DataflowTimeline } from './dataflow-timeline';
|
||||
|
||||
type LogSheetProps = IModalProps<any> & { messageId?: string };
|
||||
type LogSheetProps = IModalProps<any> & {
|
||||
isParsing: boolean;
|
||||
handleCancel(): void;
|
||||
logs?: ITraceData[];
|
||||
};
|
||||
|
||||
export function LogSheet({ hideModal, messageId }: LogSheetProps) {
|
||||
export function LogSheet({
|
||||
hideModal,
|
||||
isParsing,
|
||||
logs,
|
||||
handleCancel,
|
||||
}: LogSheetProps) {
|
||||
const { t } = useTranslation();
|
||||
const { setMessageId, data } = useFetchMessageTrace(false);
|
||||
|
||||
const { handleDownloadJson } = useDownloadOutput(data);
|
||||
|
||||
useEffect(() => {
|
||||
if (messageId) {
|
||||
setMessageId(messageId);
|
||||
}
|
||||
}, [messageId, setMessageId]);
|
||||
const { handleDownloadJson } = useDownloadOutput(logs);
|
||||
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||
<SheetContent className={cn('top-20')}>
|
||||
<SheetHeader>
|
||||
<SheetTitle className="flex items-center gap-1">
|
||||
<NotebookText className="size-4" /> {t('flow.log')}
|
||||
<SheetTitle className="flex items-center gap-2.5">
|
||||
<Logs className="size-4" /> {t('flow.log')}
|
||||
<Button variant={'ghost'}>
|
||||
{t('dataflow.viewResult')} <ArrowUpRight />
|
||||
</Button>
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<section className="max-h-[82vh] overflow-auto mt-6">
|
||||
<DataflowTimeline traceList={data}></DataflowTimeline>
|
||||
<DataflowTimeline traceList={logs}></DataflowTimeline>
|
||||
</section>
|
||||
<Button
|
||||
onClick={handleDownloadJson}
|
||||
disabled={isEndOutputEmpty(data)}
|
||||
className="w-full mt-8"
|
||||
>
|
||||
<SquareArrowOutUpRight />
|
||||
{t('dataflow.exportJson')}
|
||||
</Button>
|
||||
{isParsing ? (
|
||||
<Button
|
||||
className="w-full mt-8 bg-state-error/10 text-state-error"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
<CirclePause /> Cancel
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleDownloadJson}
|
||||
disabled={isEndOutputEmpty(logs)}
|
||||
className="w-full mt-8"
|
||||
>
|
||||
<SquareArrowOutUpRight />
|
||||
{t('dataflow.exportJson')}
|
||||
</Button>
|
||||
)}
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user