mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Displays the loading status of the data flow log #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -416,13 +416,11 @@ export const useUploadCanvasFileWithProgress = (
|
||||
return { data, loading, uploadCanvasFile: mutateAsync };
|
||||
};
|
||||
|
||||
export const useFetchMessageTrace = (
|
||||
isStopFetchTrace: boolean,
|
||||
canvasId?: string,
|
||||
) => {
|
||||
export const useFetchMessageTrace = (canvasId?: string) => {
|
||||
const { id } = useParams();
|
||||
const queryId = id || canvasId;
|
||||
const [messageId, setMessageId] = useState('');
|
||||
const [isStopFetchTrace, setISStopFetchTrace] = useState(false);
|
||||
|
||||
const {
|
||||
data,
|
||||
@ -442,11 +440,19 @@ export const useFetchMessageTrace = (
|
||||
message_id: messageId,
|
||||
});
|
||||
|
||||
return data?.data ?? [];
|
||||
return Array.isArray(data?.data) ? data?.data : [];
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, refetch, setMessageId, messageId };
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
refetch,
|
||||
setMessageId,
|
||||
messageId,
|
||||
isStopFetchTrace,
|
||||
setISStopFetchTrace,
|
||||
};
|
||||
};
|
||||
|
||||
export const useTestDbConnect = () => {
|
||||
|
||||
@ -1771,6 +1771,12 @@ Important structured information may include: names, dates, locations, events, k
|
||||
cancel: 'Cancel',
|
||||
swicthPromptMessage:
|
||||
'The prompt word will change. Please confirm whether to abandon the existing prompt word?',
|
||||
tokenizerFieldsOptions: {
|
||||
text: 'Text',
|
||||
keywords: 'Keywords',
|
||||
questions: 'Questions',
|
||||
summary: 'Augmented Context',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -23,7 +23,7 @@ import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { t } from 'i18next';
|
||||
import { get, isEmpty, isEqual, uniqWith } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import JsonView from 'react18-json-view';
|
||||
import { Operator } from '../constant';
|
||||
import { useCacheChatLog } from '../hooks/use-cache-chat-log';
|
||||
@ -116,12 +116,12 @@ export const WorkFlowTimeline = ({
|
||||
isShare,
|
||||
}: LogFlowTimelineProps) => {
|
||||
// const getNode = useGraphStore((state) => state.getNode);
|
||||
const [isStopFetchTrace, setISStopFetchTrace] = useState(false);
|
||||
|
||||
const { data: traceData, setMessageId } = useFetchMessageTrace(
|
||||
isStopFetchTrace,
|
||||
canvasId,
|
||||
);
|
||||
const {
|
||||
data: traceData,
|
||||
setMessageId,
|
||||
setISStopFetchTrace,
|
||||
} = useFetchMessageTrace(canvasId);
|
||||
|
||||
useEffect(() => {
|
||||
setMessageId(currentMessageId);
|
||||
@ -133,7 +133,7 @@ export const WorkFlowTimeline = ({
|
||||
|
||||
useEffect(() => {
|
||||
setISStopFetchTrace(!sendLoading);
|
||||
}, [sendLoading]);
|
||||
}, [sendLoading, setISStopFetchTrace]);
|
||||
|
||||
const startedNodeList = useMemo(() => {
|
||||
const finish = currentEventListWithoutMessage?.some(
|
||||
@ -151,7 +151,7 @@ export const WorkFlowTimeline = ({
|
||||
}
|
||||
return pre;
|
||||
}, []);
|
||||
}, [currentEventListWithoutMessage, sendLoading]);
|
||||
}, [currentEventListWithoutMessage, sendLoading, setISStopFetchTrace]);
|
||||
|
||||
const getElapsedTime = (nodeId: string) => {
|
||||
if (nodeId === 'begin') {
|
||||
|
||||
@ -31,12 +31,16 @@ export const FormSchema = z.object({
|
||||
|
||||
const SearchMethodOptions = buildOptions(TokenizerSearchMethod);
|
||||
|
||||
const FieldsOptions = buildOptions(TokenizerFields);
|
||||
|
||||
const TokenizerForm = ({ node }: INextOperatorForm) => {
|
||||
const { t } = useTranslation();
|
||||
const defaultValues = useFormValues(initialTokenizerValues, node);
|
||||
|
||||
const FieldsOptions = buildOptions(
|
||||
TokenizerFields,
|
||||
t,
|
||||
'dataflow.tokenizerFieldsOptions',
|
||||
);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
|
||||
@ -3,19 +3,19 @@ import { useCallback } from 'react';
|
||||
|
||||
export function useCancelCurrentDataflow({
|
||||
messageId,
|
||||
setMessageId,
|
||||
stopFetchTrace,
|
||||
}: {
|
||||
messageId: string;
|
||||
setMessageId: (messageId: string) => void;
|
||||
stopFetchTrace(): void;
|
||||
}) {
|
||||
const { cancelDataflow } = useCancelDataflow();
|
||||
|
||||
const handleCancel = useCallback(async () => {
|
||||
const code = await cancelDataflow(messageId);
|
||||
if (code === 0) {
|
||||
setMessageId('');
|
||||
stopFetchTrace();
|
||||
}
|
||||
}, [cancelDataflow, messageId, setMessageId]);
|
||||
}, [cancelDataflow, messageId, stopFetchTrace]);
|
||||
|
||||
return { handleCancel };
|
||||
}
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
export function useFetchLog() {
|
||||
const { setMessageId, data, loading, messageId } =
|
||||
useFetchMessageTrace(false);
|
||||
export function useFetchLog(logSheetVisible: boolean) {
|
||||
const {
|
||||
setMessageId,
|
||||
data,
|
||||
loading,
|
||||
messageId,
|
||||
setISStopFetchTrace,
|
||||
isStopFetchTrace,
|
||||
} = useFetchMessageTrace();
|
||||
|
||||
const isCompleted = useMemo(() => {
|
||||
if (Array.isArray(data)) {
|
||||
@ -13,18 +19,38 @@ export function useFetchLog() {
|
||||
latest?.component_id === 'END' && !isEmpty(latest?.trace[0].message)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}, [data]);
|
||||
|
||||
const isLogEmpty = !data || !data.length;
|
||||
|
||||
const stopFetchTrace = useCallback(() => {
|
||||
setISStopFetchTrace(true);
|
||||
}, [setISStopFetchTrace]);
|
||||
|
||||
// cancel request
|
||||
useEffect(() => {
|
||||
if (isCompleted) {
|
||||
stopFetchTrace();
|
||||
}
|
||||
}, [isCompleted, stopFetchTrace]);
|
||||
|
||||
useEffect(() => {
|
||||
if (logSheetVisible) {
|
||||
setISStopFetchTrace(false);
|
||||
}
|
||||
}, [logSheetVisible, setISStopFetchTrace]);
|
||||
|
||||
return {
|
||||
data,
|
||||
logs: data,
|
||||
isLogEmpty,
|
||||
isCompleted,
|
||||
loading,
|
||||
isParsing: !isLogEmpty && !isCompleted,
|
||||
isParsing: !isLogEmpty && !isCompleted && !isStopFetchTrace,
|
||||
messageId,
|
||||
setMessageId,
|
||||
stopFetchTrace,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseFetchLogReturnType = ReturnType<typeof useFetchLog>;
|
||||
|
||||
@ -89,20 +89,29 @@ export default function DataFlow() {
|
||||
hideModal: hideLogSheet,
|
||||
} = useSetModalState();
|
||||
|
||||
const { isParsing, data, messageId, setMessageId } = useFetchLog();
|
||||
const {
|
||||
isParsing,
|
||||
logs,
|
||||
messageId,
|
||||
setMessageId,
|
||||
isCompleted,
|
||||
stopFetchTrace,
|
||||
isLogEmpty,
|
||||
} = useFetchLog(logSheetVisible);
|
||||
|
||||
const handleRunAgent = useCallback(() => {
|
||||
if (isParsing) {
|
||||
// show log sheet
|
||||
showLogSheet();
|
||||
} else {
|
||||
hideLogSheet();
|
||||
handleRun();
|
||||
}
|
||||
}, [handleRun, isParsing, showLogSheet]);
|
||||
}, [handleRun, hideLogSheet, isParsing, showLogSheet]);
|
||||
|
||||
const { handleCancel } = useCancelCurrentDataflow({
|
||||
messageId,
|
||||
setMessageId,
|
||||
stopFetchTrace,
|
||||
});
|
||||
|
||||
const time = useWatchAgentChange(chatDrawerVisible);
|
||||
@ -139,7 +148,6 @@ export default function DataFlow() {
|
||||
<ButtonLoading
|
||||
variant={'secondary'}
|
||||
onClick={handleRunAgent}
|
||||
disabled={isParsing}
|
||||
loading={running}
|
||||
>
|
||||
{running || (
|
||||
@ -199,7 +207,9 @@ export default function DataFlow() {
|
||||
<LogSheet
|
||||
hideModal={hideLogSheet}
|
||||
isParsing={isParsing}
|
||||
logs={data}
|
||||
isCompleted={isCompleted}
|
||||
isLogEmpty={isLogEmpty}
|
||||
logs={logs}
|
||||
handleCancel={handleCancel}
|
||||
></LogSheet>
|
||||
)}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { File } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { Operator } from '../constant';
|
||||
@ -82,25 +83,29 @@ export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
|
||||
</div>
|
||||
</section>
|
||||
<div className="divide-y space-y-1">
|
||||
{traces.map((x, idx) => (
|
||||
<section
|
||||
key={idx}
|
||||
className="text-text-secondary text-xs space-x-2 py-2.5 !m-0"
|
||||
>
|
||||
<span>{x.datetime}</span>
|
||||
{item.component_id !== 'END' && (
|
||||
<span
|
||||
className={cn({
|
||||
'text-state-error':
|
||||
x.message.startsWith('[ERROR]'),
|
||||
})}
|
||||
>
|
||||
{x.message}
|
||||
{traces
|
||||
.filter((x) => !isEmpty(x.message))
|
||||
.map((x, idx) => (
|
||||
<section
|
||||
key={idx}
|
||||
className="text-text-secondary text-xs space-x-2 py-2.5 !m-0"
|
||||
>
|
||||
<span>{x.datetime}</span>
|
||||
{item.component_id !== 'END' && (
|
||||
<span
|
||||
className={cn({
|
||||
'text-state-error':
|
||||
x.message.startsWith('[ERROR]'),
|
||||
})}
|
||||
>
|
||||
{x.message}
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
{x.elapsed_time.toString().slice(0, 6)}s
|
||||
</span>
|
||||
)}
|
||||
<span>{x.elapsed_time.toString().slice(0, 6)}s</span>
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</TimelineContent>
|
||||
</TimelineTitle>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { SkeletonCard } from '@/components/skeleton-card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Sheet,
|
||||
@ -6,7 +7,6 @@ import {
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
ArrowUpRight,
|
||||
@ -20,19 +20,23 @@ import {
|
||||
isEndOutputEmpty,
|
||||
useDownloadOutput,
|
||||
} from '../hooks/use-download-output';
|
||||
import { UseFetchLogReturnType } from '../hooks/use-fetch-log';
|
||||
import { DataflowTimeline } from './dataflow-timeline';
|
||||
|
||||
type LogSheetProps = IModalProps<any> & {
|
||||
isParsing: boolean;
|
||||
handleCancel(): void;
|
||||
logs?: ITraceData[];
|
||||
};
|
||||
} & Pick<
|
||||
UseFetchLogReturnType,
|
||||
'isCompleted' | 'isLogEmpty' | 'isParsing' | 'logs'
|
||||
>;
|
||||
|
||||
export function LogSheet({
|
||||
hideModal,
|
||||
isParsing,
|
||||
logs,
|
||||
handleCancel,
|
||||
isCompleted,
|
||||
isLogEmpty,
|
||||
}: LogSheetProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -47,13 +51,17 @@ export function LogSheet({
|
||||
<SheetHeader>
|
||||
<SheetTitle className="flex items-center gap-2.5">
|
||||
<Logs className="size-4" /> {t('flow.log')}
|
||||
<Button variant={'ghost'}>
|
||||
<Button variant={'ghost'} disabled={!isCompleted}>
|
||||
{t('dataflow.viewResult')} <ArrowUpRight />
|
||||
</Button>
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<section className="max-h-[82vh] overflow-auto mt-6">
|
||||
<DataflowTimeline traceList={logs}></DataflowTimeline>
|
||||
{isLogEmpty ? (
|
||||
<SkeletonCard className="mt-2" />
|
||||
) : (
|
||||
<DataflowTimeline traceList={logs}></DataflowTimeline>
|
||||
)}
|
||||
</section>
|
||||
{isParsing ? (
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user