mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-14 23:06:15 +08:00
### What problem does this PR solve? feat: Automatically save agent page data #3301 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
464a4d6ead
commit
74d1eeb4d3
@ -1036,6 +1036,7 @@ The above is the content you need to summarize.`,
|
|||||||
howUseId: 'How to use agent ID?',
|
howUseId: 'How to use agent ID?',
|
||||||
content: 'Content',
|
content: 'Content',
|
||||||
operationResults: 'Operation Results',
|
operationResults: 'Operation Results',
|
||||||
|
autosave: 'Automatically saved',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -984,6 +984,7 @@ export default {
|
|||||||
howUseId: '如何使用Agent ID?',
|
howUseId: '如何使用Agent ID?',
|
||||||
content: '內容',
|
content: '內容',
|
||||||
operationResults: '運行結果',
|
operationResults: '運行結果',
|
||||||
|
autosave: '已自動儲存',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: '“保留所有權利 @ react”',
|
profile: '“保留所有權利 @ react”',
|
||||||
|
@ -1004,6 +1004,7 @@ export default {
|
|||||||
howUseId: '如何使用Agent ID?',
|
howUseId: '如何使用Agent ID?',
|
||||||
content: '内容',
|
content: '内容',
|
||||||
operationResults: '运行结果',
|
operationResults: '运行结果',
|
||||||
|
autosave: '已自动保存',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -128,6 +128,9 @@ function FlowCanvas({ chatDrawerVisible, hideChatDrawer }: IProps) {
|
|||||||
onSelectionChange={onSelectionChange}
|
onSelectionChange={onSelectionChange}
|
||||||
nodeOrigin={[0.5, 0]}
|
nodeOrigin={[0.5, 0]}
|
||||||
isValidConnection={isValidConnection}
|
isValidConnection={isValidConnection}
|
||||||
|
onChangeCapture={(...params) => {
|
||||||
|
console.info('onChangeCapture:', ...params);
|
||||||
|
}}
|
||||||
onChange={(...params) => {
|
onChange={(...params) => {
|
||||||
console.info('params:', ...params);
|
console.info('params:', ...params);
|
||||||
}}
|
}}
|
||||||
@ -140,18 +143,6 @@ function FlowCanvas({ chatDrawerVisible, hideChatDrawer }: IProps) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
deleteKeyCode={['Delete', 'Backspace']}
|
deleteKeyCode={['Delete', 'Backspace']}
|
||||||
onPaste={(...params) => {
|
|
||||||
console.info('onPaste:', ...params);
|
|
||||||
}}
|
|
||||||
onPasteCapture={(...params) => {
|
|
||||||
console.info('onPasteCapture:', ...params);
|
|
||||||
}}
|
|
||||||
onCopy={(...params) => {
|
|
||||||
console.info('onCopy:', ...params);
|
|
||||||
}}
|
|
||||||
onCopyCapture={(...params) => {
|
|
||||||
console.info('onCopyCapture:', ...params);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Background />
|
<Background />
|
||||||
<Controls />
|
<Controls />
|
||||||
|
@ -5,12 +5,15 @@ import { NodeData } from '../../interface';
|
|||||||
import { RightHandleStyle } from './handle-icon';
|
import { RightHandleStyle } from './handle-icon';
|
||||||
|
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
import { useReplaceIdWithName } from '../../hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import NodeHeader from './node-header';
|
import NodeHeader from './node-header';
|
||||||
|
|
||||||
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
||||||
const yes = get(data, 'form.yes');
|
const yes = get(data, 'form.yes');
|
||||||
const no = get(data, 'form.no');
|
const no = get(data, 'form.no');
|
||||||
|
const replaceIdWithName = useReplaceIdWithName();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={classNames(styles.logicNode, {
|
className={classNames(styles.logicNode, {
|
||||||
@ -50,11 +53,11 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
|||||||
<Flex vertical gap={10}>
|
<Flex vertical gap={10}>
|
||||||
<Flex vertical>
|
<Flex vertical>
|
||||||
<div className={styles.relevantLabel}>Yes</div>
|
<div className={styles.relevantLabel}>Yes</div>
|
||||||
<div className={styles.nodeText}>{yes}</div>
|
<div className={styles.nodeText}>{replaceIdWithName(yes)}</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex vertical>
|
<Flex vertical>
|
||||||
<div className={styles.relevantLabel}>No</div>
|
<div className={styles.relevantLabel}>No</div>
|
||||||
<div className={styles.nodeText}>{no}</div>
|
<div className={styles.nodeText}>{replaceIdWithName(no)}</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
.flowHeader {
|
.flowHeader {
|
||||||
padding: 0 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@ import { ArrowLeftOutlined } from '@ant-design/icons';
|
|||||||
import { Button, Flex, Space } from 'antd';
|
import { Button, Flex, Space } from 'antd';
|
||||||
import { Link, useParams } from 'umi';
|
import { Link, useParams } from 'umi';
|
||||||
import FlowIdModal from '../flow-id-modal';
|
import FlowIdModal from '../flow-id-modal';
|
||||||
import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
|
import {
|
||||||
|
useSaveGraph,
|
||||||
|
useSaveGraphBeforeOpeningDebugDrawer,
|
||||||
|
useWatchAgentChange,
|
||||||
|
} from '../hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -20,10 +24,11 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
|
|||||||
const {
|
const {
|
||||||
visible: overviewVisible,
|
visible: overviewVisible,
|
||||||
hideModal: hideOverviewModal,
|
hideModal: hideOverviewModal,
|
||||||
showModal: showOverviewModal,
|
// showModal: showOverviewModal,
|
||||||
} = useSetModalState();
|
} = useSetModalState();
|
||||||
const { visible, hideModal, showModal } = useSetModalState();
|
const { visible, hideModal, showModal } = useSetModalState();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const time = useWatchAgentChange();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -37,7 +42,10 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
|
|||||||
<Link to={`/flow`}>
|
<Link to={`/flow`}>
|
||||||
<ArrowLeftOutlined />
|
<ArrowLeftOutlined />
|
||||||
</Link>
|
</Link>
|
||||||
<h3>{data.title}</h3>
|
<div className="flex flex-col">
|
||||||
|
<span className="font-semibold text-[18px]">{data.title}</span>
|
||||||
|
<span className="font-normal text-sm">已自动保存 {time}</span>
|
||||||
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
<Space size={'large'}>
|
<Space size={'large'}>
|
||||||
<Button onClick={handleRun}>
|
<Button onClick={handleRun}>
|
||||||
|
@ -19,7 +19,9 @@ import {
|
|||||||
import { useFetchModelId, useSendMessageWithSse } from '@/hooks/logic-hooks';
|
import { useFetchModelId, useSendMessageWithSse } from '@/hooks/logic-hooks';
|
||||||
import { Variable } from '@/interfaces/database/chat';
|
import { Variable } from '@/interfaces/database/chat';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
|
import { useDebounceEffect } from 'ahooks';
|
||||||
import { FormInstance, message } from 'antd';
|
import { FormInstance, message } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import { humanId } from 'human-id';
|
import { humanId } from 'human-id';
|
||||||
import { lowerFirst } from 'lodash';
|
import { lowerFirst } from 'lodash';
|
||||||
import trim from 'lodash/trim';
|
import trim from 'lodash/trim';
|
||||||
@ -446,12 +448,21 @@ export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => {
|
|||||||
return handleRun;
|
return handleRun;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useReplaceIdWithText = (output: unknown) => {
|
export const useReplaceIdWithName = () => {
|
||||||
const getNode = useGraphStore((state) => state.getNode);
|
const getNode = useGraphStore((state) => state.getNode);
|
||||||
|
|
||||||
const getNameById = (id?: string) => {
|
const replaceIdWithName = useCallback(
|
||||||
return getNode(id)?.data.name;
|
(id?: string) => {
|
||||||
};
|
return getNode(id)?.data.name;
|
||||||
|
},
|
||||||
|
[getNode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return replaceIdWithName;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useReplaceIdWithText = (output: unknown) => {
|
||||||
|
const getNameById = useReplaceIdWithName();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
replacedOutput: replaceIdWithText(output, getNameById),
|
replacedOutput: replaceIdWithText(output, getNameById),
|
||||||
@ -547,6 +558,7 @@ export const useWatchNodeFormDataChange = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.info('xxx');
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const currentNode = getNode(node.id);
|
const currentNode = getNode(node.id);
|
||||||
const form = currentNode?.data.form ?? {};
|
const form = currentNode?.data.form ?? {};
|
||||||
@ -668,3 +680,36 @@ export const useCopyPaste = () => {
|
|||||||
};
|
};
|
||||||
}, [onPasteCapture]);
|
}, [onPasteCapture]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useWatchAgentChange = () => {
|
||||||
|
const [time, setTime] = useState<string>();
|
||||||
|
const nodes = useGraphStore((state) => state.nodes);
|
||||||
|
const edges = useGraphStore((state) => state.edges);
|
||||||
|
const { saveGraph } = useSaveGraph();
|
||||||
|
const { data: flowDetail } = useFetchFlow();
|
||||||
|
|
||||||
|
const setSaveTime = useCallback((updateTime: number) => {
|
||||||
|
setTime(dayjs(updateTime).format('YYYY-MM-DD HH:mm:ss'));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSaveTime(flowDetail?.update_time);
|
||||||
|
}, [flowDetail, setSaveTime]);
|
||||||
|
|
||||||
|
const saveAgent = useCallback(async () => {
|
||||||
|
const ret = await saveGraph();
|
||||||
|
setSaveTime(ret.data.update_time);
|
||||||
|
}, [saveGraph, setSaveTime]);
|
||||||
|
|
||||||
|
useDebounceEffect(
|
||||||
|
() => {
|
||||||
|
saveAgent();
|
||||||
|
},
|
||||||
|
[nodes, edges],
|
||||||
|
{
|
||||||
|
wait: 1000 * 20,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return time;
|
||||||
|
};
|
||||||
|
@ -24,6 +24,7 @@ import { immer } from 'zustand/middleware/immer';
|
|||||||
import { Operator, SwitchElseTo } from './constant';
|
import { Operator, SwitchElseTo } from './constant';
|
||||||
import { NodeData } from './interface';
|
import { NodeData } from './interface';
|
||||||
import {
|
import {
|
||||||
|
duplicateNodeForm,
|
||||||
generateNodeNamesWithIncreasingIndex,
|
generateNodeNamesWithIncreasingIndex,
|
||||||
getNodeDragHandle,
|
getNodeDragHandle,
|
||||||
getOperatorIndex,
|
getOperatorIndex,
|
||||||
@ -242,7 +243,10 @@ const useGraphStore = create<RFState>()(
|
|||||||
|
|
||||||
addNode({
|
addNode({
|
||||||
...(node || {}),
|
...(node || {}),
|
||||||
data: { ...(node?.data ?? {}), name: generateNodeName(name) },
|
data: {
|
||||||
|
...duplicateNodeForm(node?.data),
|
||||||
|
name: generateNodeName(name),
|
||||||
|
},
|
||||||
selected: false,
|
selected: false,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
id: `${node?.data?.label}:${humanId()}`,
|
id: `${node?.data?.label}:${humanId()}`,
|
||||||
|
@ -289,3 +289,31 @@ export const generateNodeNamesWithIncreasingIndex = (
|
|||||||
|
|
||||||
return `${name}_${index}`;
|
return `${name}_${index}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const duplicateNodeForm = (nodeData?: NodeData) => {
|
||||||
|
const form: Record<string, any> = { ...(nodeData?.form ?? {}) };
|
||||||
|
|
||||||
|
// Delete the downstream node corresponding to the to field of the Categorize operator
|
||||||
|
if (nodeData?.label === Operator.Categorize) {
|
||||||
|
form.category_description = Object.keys(form.category_description).reduce<
|
||||||
|
Record<string, Record<string, any>>
|
||||||
|
>((pre, cur) => {
|
||||||
|
pre[cur] = {
|
||||||
|
...form.category_description[cur],
|
||||||
|
to: undefined,
|
||||||
|
};
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the downstream nodes corresponding to the yes and no fields of the Relevant operator
|
||||||
|
if (nodeData?.label === Operator.Relevant) {
|
||||||
|
form.yes = undefined;
|
||||||
|
form.no = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(nodeData ?? {}),
|
||||||
|
form,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user