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