Feat: Cancel a running data flow test #9869 (#10257)

### 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:
balibabu
2025-09-24 16:33:33 +08:00
committed by GitHub
parent 5715ca6b74
commit 6bf0cda16f
17 changed files with 251 additions and 84 deletions

View File

@ -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>

View File

@ -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>
);