mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-26 00:46:52 +08:00
Fix: Improve Agent templates functionality and fix some UI style issues (#9129)
### What problem does this PR solve? Fix: Improve Agent templates functionality and fix some UI style issues #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -13,7 +13,7 @@ import {
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { NotebookPen } from 'lucide-react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChatSheet } from '../chat/chat-sheet';
|
||||
import { AgentBackground } from '../components/background';
|
||||
@ -132,6 +132,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({
|
||||
setCurrentMessageId,
|
||||
});
|
||||
const [lastSendLoading, setLastSendLoading] = useState(false);
|
||||
|
||||
const { handleBeforeDelete } = useBeforeDelete();
|
||||
|
||||
@ -152,7 +153,13 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
clearEventList();
|
||||
}
|
||||
}, [chatVisible, clearEventList]);
|
||||
|
||||
const setLastSendLoadingFunc = (loading: boolean, messageId: string) => {
|
||||
if (messageId === currentMessageId) {
|
||||
setLastSendLoading(loading);
|
||||
} else {
|
||||
setLastSendLoading(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={styles.canvasWrapper}>
|
||||
<svg
|
||||
@ -243,7 +250,9 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
</AgentInstanceContext.Provider>
|
||||
)}
|
||||
{chatVisible && (
|
||||
<AgentChatContext.Provider value={{ showLogSheet }}>
|
||||
<AgentChatContext.Provider
|
||||
value={{ showLogSheet, setLastSendLoadingFunc }}
|
||||
>
|
||||
<AgentChatLogContext.Provider
|
||||
value={{ addEventList, setCurrentMessageId }}
|
||||
>
|
||||
@ -264,6 +273,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
currentEventListWithoutMessageById
|
||||
}
|
||||
currentMessageId={currentMessageId}
|
||||
sendLoading={lastSendLoading}
|
||||
></LogSheet>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -251,12 +251,12 @@ export const useSendAgentMessage = (
|
||||
},
|
||||
[
|
||||
agentId,
|
||||
sessionId,
|
||||
send,
|
||||
clearUploadResponseList,
|
||||
inputs,
|
||||
beginParams,
|
||||
uploadResponseList,
|
||||
sessionId,
|
||||
setValue,
|
||||
removeLatestMessage,
|
||||
],
|
||||
|
||||
@ -22,7 +22,7 @@ export const AgentInstanceContext = createContext<AgentInstanceContextType>(
|
||||
type AgentChatContextType = Pick<
|
||||
ReturnType<typeof useShowLogSheet>,
|
||||
'showLogSheet'
|
||||
>;
|
||||
> & { setLastSendLoadingFunc: (loading: boolean, messageId: string) => void };
|
||||
|
||||
export const AgentChatContext = createContext<AgentChatContextType>(
|
||||
{} as AgentChatContextType,
|
||||
|
||||
@ -123,7 +123,7 @@ function BeginForm({ node }: INextOperatorForm) {
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{enablePrologue && (
|
||||
{mode === AgentDialogueMode.Conversational && enablePrologue && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'prologue'}
|
||||
@ -175,7 +175,6 @@ function BeginForm({ node }: INextOperatorForm) {
|
||||
deleteRecord={handleDeleteRecord}
|
||||
></QueryTable>
|
||||
</Collapse>
|
||||
|
||||
{visible && (
|
||||
<ParameterDialog
|
||||
hideModal={hideModal}
|
||||
|
||||
@ -17,10 +17,11 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { ChangeEvent, useEffect, useMemo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
@ -41,6 +42,7 @@ function ParameterForm({
|
||||
otherThanCurrentQuery,
|
||||
submit,
|
||||
}: ModalFormProps) {
|
||||
const { t } = useTranslate('flow');
|
||||
const FormSchema = z.object({
|
||||
type: z.string(),
|
||||
key: z
|
||||
@ -84,7 +86,7 @@ function ParameterForm({
|
||||
<Icon
|
||||
className={`size-${cur === BeginQueryType.Options ? 4 : 5}`}
|
||||
></Icon>
|
||||
{cur}
|
||||
{t(cur.toLowerCase())}
|
||||
</div>
|
||||
),
|
||||
value: cur,
|
||||
@ -116,6 +118,13 @@ function ParameterForm({
|
||||
submit(values);
|
||||
}
|
||||
|
||||
const handleKeyChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const name = form.getValues().name || '';
|
||||
form.setValue('key', e.target.value.trim());
|
||||
if (!name) {
|
||||
form.setValue('name', e.target.value.trim());
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
@ -144,7 +153,7 @@ function ParameterForm({
|
||||
<FormItem>
|
||||
<FormLabel>Key</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} autoComplete="off" />
|
||||
<Input {...field} autoComplete="off" onBlur={handleKeyChange} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@ -53,7 +53,7 @@ export function QueryTable({ data = [], deleteRecord, showModal }: IProps) {
|
||||
const columns: ColumnDef<BeginQuery>[] = [
|
||||
{
|
||||
accessorKey: 'key',
|
||||
header: 'key',
|
||||
header: 'Key',
|
||||
meta: { cellClassName: 'max-w-30' },
|
||||
cell: ({ row }) => {
|
||||
const key: string = row.getValue('key');
|
||||
@ -90,7 +90,11 @@ export function QueryTable({ data = [], deleteRecord, showModal }: IProps) {
|
||||
{
|
||||
accessorKey: 'type',
|
||||
header: t('flow.type'),
|
||||
cell: ({ row }) => <div>{row.getValue('type')}</div>,
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
{t(`flow.${(row.getValue('type')?.toString() || '').toLowerCase()}`)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'optional',
|
||||
|
||||
@ -14,12 +14,13 @@ type LogSheetProps = IModalProps<any> &
|
||||
Pick<
|
||||
ReturnType<typeof useCacheChatLog>,
|
||||
'currentEventListWithoutMessageById' | 'currentMessageId'
|
||||
>;
|
||||
> & { sendLoading: boolean };
|
||||
|
||||
export function LogSheet({
|
||||
hideModal,
|
||||
currentEventListWithoutMessageById,
|
||||
currentMessageId,
|
||||
sendLoading,
|
||||
}: LogSheetProps) {
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||
@ -36,6 +37,7 @@ export function LogSheet({
|
||||
currentMessageId,
|
||||
)}
|
||||
currentMessageId={currentMessageId}
|
||||
sendLoading={sendLoading}
|
||||
/>
|
||||
</section>
|
||||
</SheetContent>
|
||||
|
||||
@ -16,7 +16,13 @@ import { Operator } from '../constant';
|
||||
import OperatorIcon from '../operator-icon';
|
||||
import { JsonViewer } from './workFlowTimeline';
|
||||
|
||||
const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
const ToolTimelineItem = ({
|
||||
tools,
|
||||
sendLoading = false,
|
||||
}: {
|
||||
tools: Record<string, any>[];
|
||||
sendLoading: boolean;
|
||||
}) => {
|
||||
if (!tools || tools.length === 0 || !Array.isArray(tools)) return null;
|
||||
const blackList = ['add_memory', 'gen_citations'];
|
||||
const filteredTools = tools.filter(
|
||||
@ -32,6 +38,15 @@ const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
})
|
||||
.join(' ');
|
||||
};
|
||||
const parentName = (str: string, separator: string = '-->') => {
|
||||
if (!str) return '';
|
||||
const strs = str.split(separator);
|
||||
if (strs.length > 1) {
|
||||
return strs[strs.length - 1];
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{filteredTools?.map((tool, idx) => {
|
||||
@ -58,7 +73,9 @@ const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
'group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 p-1 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7',
|
||||
{
|
||||
'border border-blue-500': !(
|
||||
idx >= filteredTools.length - 1 && tool.result === '...'
|
||||
idx >= filteredTools.length - 1 &&
|
||||
tool.result === '...' &&
|
||||
sendLoading
|
||||
),
|
||||
},
|
||||
)}
|
||||
@ -69,7 +86,8 @@ const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
className={cn('rounded-full w-6 h-6', {
|
||||
' border-muted-foreground border-2 border-t-transparent animate-spin ':
|
||||
idx >= filteredTools.length - 1 &&
|
||||
tool.result === '...',
|
||||
tool.result === '...' &&
|
||||
sendLoading,
|
||||
})}
|
||||
></div>
|
||||
</div>
|
||||
@ -93,7 +111,7 @@ const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>
|
||||
{tool.path + ' '}
|
||||
{parentName(tool.path) + ' '}
|
||||
{capitalizeWords(tool.tool_name, '_')}
|
||||
</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
} from '@/hooks/use-send-message';
|
||||
import { ITraceData } from '@/interfaces/database/agent';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { t } from 'i18next';
|
||||
import { get } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import JsonView from 'react18-json-view';
|
||||
@ -30,7 +31,7 @@ import ToolTimelineItem from './toolTimelineItem';
|
||||
type LogFlowTimelineProps = Pick<
|
||||
ReturnType<typeof useCacheChatLog>,
|
||||
'currentEventListWithoutMessage' | 'currentMessageId'
|
||||
> & { canvasId?: string };
|
||||
> & { canvasId?: string; sendLoading: boolean };
|
||||
export function JsonViewer({
|
||||
data,
|
||||
title,
|
||||
@ -67,6 +68,7 @@ export const WorkFlowTimeline = ({
|
||||
currentEventListWithoutMessage,
|
||||
currentMessageId,
|
||||
canvasId,
|
||||
sendLoading,
|
||||
}: LogFlowTimelineProps) => {
|
||||
// const getNode = useGraphStore((state) => state.getNode);
|
||||
const [isStopFetchTrace, setISStopFetchTrace] = useState(false);
|
||||
@ -79,31 +81,20 @@ export const WorkFlowTimeline = ({
|
||||
useEffect(() => {
|
||||
setMessageId(currentMessageId);
|
||||
}, [currentMessageId, setMessageId]);
|
||||
// const getNodeName = useCallback(
|
||||
// (nodeId: string) => {
|
||||
// if ('begin' === nodeId) return t('flow.begin');
|
||||
// return getNode(nodeId)?.data.name;
|
||||
// },
|
||||
// [getNode],
|
||||
// );
|
||||
// const getNodeById = useCallback(
|
||||
// (nodeId: string) => {
|
||||
// const data = currentEventListWithoutMessage
|
||||
// .map((x) => x.data)
|
||||
// .filter((x) => x.component_id === nodeId);
|
||||
// if ('begin' === nodeId) return t('flow.begin');
|
||||
// if (data && data.length) {
|
||||
// return data[0];
|
||||
// }
|
||||
// return {};
|
||||
// },
|
||||
// [currentEventListWithoutMessage],
|
||||
// );
|
||||
const getNodeName = (nodeId: string) => {
|
||||
if ('begin' === nodeId) return t('flow.begin');
|
||||
return nodeId;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setISStopFetchTrace(!sendLoading);
|
||||
}, [sendLoading]);
|
||||
|
||||
const startedNodeList = useMemo(() => {
|
||||
const finish = currentEventListWithoutMessage?.some(
|
||||
(item) => item.event === MessageEventType.WorkflowFinished,
|
||||
);
|
||||
setISStopFetchTrace(finish);
|
||||
setISStopFetchTrace(finish || !sendLoading);
|
||||
const duplicateList = currentEventListWithoutMessage?.filter(
|
||||
(x) => x.event === MessageEventType.NodeStarted,
|
||||
) as INodeEvent[];
|
||||
@ -115,7 +106,7 @@ export const WorkFlowTimeline = ({
|
||||
}
|
||||
return pre;
|
||||
}, []);
|
||||
}, [currentEventListWithoutMessage]);
|
||||
}, [currentEventListWithoutMessage, sendLoading]);
|
||||
|
||||
const hasTrace = useCallback(
|
||||
(componentId: string) => {
|
||||
@ -198,7 +189,8 @@ export const WorkFlowTimeline = ({
|
||||
<div
|
||||
className={cn('rounded-full w-6 h-6', {
|
||||
' border-muted-foreground border-2 border-t-transparent animate-spin ':
|
||||
!finishNodeIds.includes(x.data.component_id),
|
||||
!finishNodeIds.includes(x.data.component_id) &&
|
||||
sendLoading,
|
||||
})}
|
||||
></div>
|
||||
</div>
|
||||
@ -212,7 +204,7 @@ export const WorkFlowTimeline = ({
|
||||
</TimelineIndicator>
|
||||
</TimelineHeader>
|
||||
<TimelineContent className="text-foreground rounded-lg border mb-5">
|
||||
<section key={idx}>
|
||||
<section key={'content_' + idx}>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
@ -221,7 +213,7 @@ export const WorkFlowTimeline = ({
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{x.data?.component_name}</span>
|
||||
<span>{getNodeName(x.data?.component_name)}</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
</span>
|
||||
@ -253,7 +245,9 @@ export const WorkFlowTimeline = ({
|
||||
</TimelineItem>
|
||||
{hasTrace(x.data.component_id) && (
|
||||
<ToolTimelineItem
|
||||
key={'tool_' + idx}
|
||||
tools={filterTrace(x.data.component_id)}
|
||||
sendLoading={sendLoading}
|
||||
></ToolTimelineItem>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user