mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-23 15:06:50 +08:00
### What problem does this PR solve? Feat: Add note node #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -19,11 +19,15 @@ import {
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||
import { ILogEvent, MessageEventType } from '@/hooks/use-send-message';
|
||||
import {
|
||||
INodeData,
|
||||
INodeEvent,
|
||||
MessageEventType,
|
||||
} from '@/hooks/use-send-message';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
import { BellElectric, NotebookText } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import JsonView from 'react18-json-view';
|
||||
@ -57,25 +61,19 @@ function JsonViewer({
|
||||
);
|
||||
}
|
||||
|
||||
function concatData(
|
||||
firstRecord: Record<string, any> | Array<Record<string, any>>,
|
||||
nextRecord: Record<string, any> | Array<Record<string, any>>,
|
||||
function getInputsOrOutputs(
|
||||
nodeEventList: INodeData[],
|
||||
field: 'inputs' | 'outputs',
|
||||
) {
|
||||
let result: Array<Record<string, any>> = [];
|
||||
const inputsOrOutputs = nodeEventList.map((x) => get(x, field, {}));
|
||||
|
||||
if (!isEmpty(firstRecord)) {
|
||||
result = result.concat(firstRecord);
|
||||
if (inputsOrOutputs.length < 2) {
|
||||
return inputsOrOutputs[0] || {};
|
||||
}
|
||||
|
||||
if (!isEmpty(nextRecord)) {
|
||||
result = result.concat(nextRecord);
|
||||
}
|
||||
|
||||
return isEmpty(result) ? {} : result;
|
||||
return inputsOrOutputs;
|
||||
}
|
||||
|
||||
type EventWithIndex = { startNodeIdx: number } & ILogEvent;
|
||||
|
||||
export function LogSheet({
|
||||
hideModal,
|
||||
currentEventListWithoutMessage,
|
||||
@ -96,68 +94,58 @@ export function LogSheet({
|
||||
[getNode],
|
||||
);
|
||||
|
||||
const startedNodeList = useMemo(() => {
|
||||
const duplicateList = currentEventListWithoutMessage.filter(
|
||||
(x) => x.event === MessageEventType.NodeStarted,
|
||||
) as INodeEvent[];
|
||||
|
||||
// Remove duplicate nodes
|
||||
return duplicateList.reduce<Array<INodeEvent>>((pre, cur) => {
|
||||
if (pre.every((x) => x.data.component_id !== cur.data.component_id)) {
|
||||
pre.push(cur);
|
||||
}
|
||||
return pre;
|
||||
}, []);
|
||||
}, [currentEventListWithoutMessage]);
|
||||
|
||||
const hasTrace = useCallback(
|
||||
(componentId: string) => {
|
||||
if (Array.isArray(traceData)) {
|
||||
return traceData?.some((x) => x.component_id === componentId);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[traceData],
|
||||
);
|
||||
|
||||
const filterTrace = useCallback(
|
||||
(componentId: string) => {
|
||||
return traceData
|
||||
const trace = traceData
|
||||
?.filter((x) => x.component_id === componentId)
|
||||
.reduce<ITraceData['trace']>((pre, cur) => {
|
||||
pre.push(...cur.trace);
|
||||
|
||||
return pre;
|
||||
}, []);
|
||||
return Array.isArray(trace) ? trace : {};
|
||||
},
|
||||
[traceData],
|
||||
);
|
||||
|
||||
// Look up to find the nearest start component id and concatenate the finish and log data into one
|
||||
const finishedNodeList = useMemo(() => {
|
||||
return currentEventListWithoutMessage.filter(
|
||||
(x) =>
|
||||
x.event === MessageEventType.NodeFinished ||
|
||||
x.event === MessageEventType.NodeLogs,
|
||||
) as ILogEvent[];
|
||||
}, [currentEventListWithoutMessage]);
|
||||
const filterFinishedNodeList = useCallback(
|
||||
(componentId: string) => {
|
||||
const nodeEventList = currentEventListWithoutMessage
|
||||
.filter(
|
||||
(x) =>
|
||||
x.event === MessageEventType.NodeFinished &&
|
||||
(x.data as INodeData)?.component_id === componentId,
|
||||
)
|
||||
.map((x) => x.data);
|
||||
|
||||
const nextList = useMemo(() => {
|
||||
return finishedNodeList.reduce<Array<EventWithIndex>>((pre, cur) => {
|
||||
const startNodeIdx = (
|
||||
currentEventListWithoutMessage as Array<ILogEvent>
|
||||
).findLastIndex(
|
||||
(x) =>
|
||||
x.data.component_id === cur.data.component_id &&
|
||||
x.event === MessageEventType.NodeStarted,
|
||||
);
|
||||
|
||||
const item = pre.find((x) => x.startNodeIdx === startNodeIdx);
|
||||
|
||||
const { inputs = {}, outputs = {} } = cur.data;
|
||||
if (item) {
|
||||
const { inputs: inputList, outputs: outputList } = item.data;
|
||||
|
||||
item.data = {
|
||||
...item.data,
|
||||
inputs: concatData(inputList, inputs),
|
||||
outputs: concatData(outputList, outputs),
|
||||
};
|
||||
} else {
|
||||
pre.push({
|
||||
...cur,
|
||||
startNodeIdx,
|
||||
});
|
||||
}
|
||||
|
||||
return pre;
|
||||
}, []);
|
||||
}, [currentEventListWithoutMessage, finishedNodeList]);
|
||||
return nodeEventList;
|
||||
},
|
||||
[currentEventListWithoutMessage],
|
||||
);
|
||||
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||
@ -170,76 +158,75 @@ export function LogSheet({
|
||||
</SheetHeader>
|
||||
<section className="max-h-[82vh] overflow-auto mt-6">
|
||||
<Timeline>
|
||||
{nextList.map((x, idx) => (
|
||||
<TimelineItem
|
||||
key={idx}
|
||||
step={idx}
|
||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
|
||||
>
|
||||
<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-6.5 top-6 bg-background-checked" />
|
||||
{startedNodeList.map((x, idx) => {
|
||||
const nodeDataList = filterFinishedNodeList(x.data.component_id);
|
||||
const inputs = getInputsOrOutputs(nodeDataList, 'inputs');
|
||||
const outputs = getInputsOrOutputs(nodeDataList, 'outputs');
|
||||
return (
|
||||
<TimelineItem
|
||||
key={idx}
|
||||
step={idx}
|
||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
|
||||
>
|
||||
<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-6.5 top-6 bg-background-checked" />
|
||||
|
||||
<TimelineIndicator className="bg-primary/10 group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center border-none group-data-[orientation=vertical]/timeline:-left-7">
|
||||
<BellElectric className="size-5" />
|
||||
{/* <img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
className="size-6 rounded-full"
|
||||
/> */}
|
||||
</TimelineIndicator>
|
||||
</TimelineHeader>
|
||||
<TimelineContent className="text-foreground rounded-lg border mb-5">
|
||||
<section key={idx}>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="bg-background-card px-3"
|
||||
>
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{getNodeName(x.data?.component_id)}</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'border-background -end-1 -top-1 size-2 rounded-full border-2 bg-dot-green',
|
||||
{ 'text-dot-green': x.data.error === null },
|
||||
{ 'text-dot-red': x.data.error !== null },
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Online</span>
|
||||
</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="space-y-2">
|
||||
<JsonViewer
|
||||
data={x.data.inputs}
|
||||
title="Input"
|
||||
></JsonViewer>
|
||||
|
||||
{hasTrace(x.data.component_id) && (
|
||||
<TimelineIndicator className="bg-primary/10 group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center border-none group-data-[orientation=vertical]/timeline:-left-7">
|
||||
<BellElectric className="size-5" />
|
||||
</TimelineIndicator>
|
||||
</TimelineHeader>
|
||||
<TimelineContent className="text-foreground rounded-lg border mb-5">
|
||||
<section key={idx}>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="bg-background-card px-3"
|
||||
>
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{getNodeName(x.data?.component_id)}</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'border-background -end-1 -top-1 size-2 rounded-full border-2 bg-dot-green',
|
||||
{ 'text-dot-green': x.data.error === null },
|
||||
{ 'text-dot-red': x.data.error !== null },
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Online</span>
|
||||
</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="space-y-2">
|
||||
<JsonViewer
|
||||
data={filterTrace(x.data.component_id) ?? {}}
|
||||
title={'Trace'}
|
||||
data={inputs}
|
||||
title="Input"
|
||||
></JsonViewer>
|
||||
)}
|
||||
|
||||
<JsonViewer
|
||||
data={x.data.outputs}
|
||||
title={'Output'}
|
||||
></JsonViewer>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</section>
|
||||
{/* <TimelineDate className="mt-1 mb-0">{item.date}</TimelineDate> */}
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
))}
|
||||
{hasTrace(x.data.component_id) && (
|
||||
<JsonViewer
|
||||
data={filterTrace(x.data.component_id)}
|
||||
title={'Trace'}
|
||||
></JsonViewer>
|
||||
)}
|
||||
|
||||
<JsonViewer
|
||||
data={outputs}
|
||||
title={'Output'}
|
||||
></JsonViewer>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</section>
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
);
|
||||
})}
|
||||
</Timeline>
|
||||
</section>
|
||||
</SheetContent>
|
||||
|
||||
Reference in New Issue
Block a user