balibabu commited on
Commit
8441328
·
1 Parent(s): 907a1f4

feat: add OperateDropdown and send debug message #918 (#1095)

Browse files

### What problem does this PR solve?
feat: add OperateDropdown
feat: send debug message #918

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/operate-dropdown/index.less ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .delete {
2
+ height: 24px;
3
+ }
web/src/components/operate-dropdown/index.tsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg';
2
+ import { useShowDeleteConfirm } from '@/hooks/commonHooks';
3
+ import { DeleteOutlined } from '@ant-design/icons';
4
+ import { Dropdown, MenuProps, Space } from 'antd';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import React from 'react';
8
+ import styles from './index.less';
9
+
10
+ interface IProps {
11
+ deleteItem: () => Promise<any>;
12
+ }
13
+
14
+ const OperateDropdown = ({
15
+ deleteItem,
16
+ children,
17
+ }: React.PropsWithChildren<IProps>) => {
18
+ const { t } = useTranslation();
19
+ const showDeleteConfirm = useShowDeleteConfirm();
20
+
21
+ const handleDelete = () => {
22
+ showDeleteConfirm({ onOk: deleteItem });
23
+ };
24
+
25
+ const handleDropdownMenuClick: MenuProps['onClick'] = ({ domEvent, key }) => {
26
+ domEvent.preventDefault();
27
+ domEvent.stopPropagation();
28
+ if (key === '1') {
29
+ handleDelete();
30
+ }
31
+ };
32
+
33
+ const items: MenuProps['items'] = [
34
+ {
35
+ key: '1',
36
+ label: (
37
+ <Space>
38
+ {t('common.delete')}
39
+ <DeleteOutlined />
40
+ </Space>
41
+ ),
42
+ },
43
+ ];
44
+
45
+ return (
46
+ <Dropdown
47
+ menu={{
48
+ items,
49
+ onClick: handleDropdownMenuClick,
50
+ }}
51
+ >
52
+ {children || (
53
+ <span className={styles.delete}>
54
+ <MoreIcon />
55
+ </span>
56
+ )}
57
+ </Dropdown>
58
+ );
59
+ };
60
+
61
+ export default OperateDropdown;
web/src/hooks/flow-hooks.ts CHANGED
@@ -64,7 +64,7 @@ export const useSetFlow = () => {
64
  );
65
  queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
66
  }
67
- return data?.retcode;
68
  },
69
  });
70
 
 
64
  );
65
  queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
66
  }
67
+ return data;
68
  },
69
  });
70
 
web/src/hooks/logicHooks.ts CHANGED
@@ -206,14 +206,14 @@ export const useSendMessageWithSse = (
206
 
207
  //#region chat hooks
208
 
209
- export const useScrollToBottom = (id?: string) => {
210
  const ref = useRef<HTMLDivElement>(null);
211
 
212
  const scrollToBottom = useCallback(() => {
213
- if (id) {
214
  ref.current?.scrollIntoView({ behavior: 'instant' });
215
  }
216
- }, [id]);
217
 
218
  useEffect(() => {
219
  scrollToBottom();
 
206
 
207
  //#region chat hooks
208
 
209
+ export const useScrollToBottom = (messages?: unknown) => {
210
  const ref = useRef<HTMLDivElement>(null);
211
 
212
  const scrollToBottom = useCallback(() => {
213
+ if (messages) {
214
  ref.current?.scrollIntoView({ behavior: 'instant' });
215
  }
216
+ }, [messages]); // If the message changes, scroll to the bottom
217
 
218
  useEffect(() => {
219
  scrollToBottom();
web/src/interfaces/database/flow.ts CHANGED
@@ -1,4 +1,5 @@
1
  import { Edge, Node } from 'reactflow';
 
2
 
3
  export type DSLComponents = Record<string, IOperator>;
4
 
@@ -8,6 +9,8 @@ export interface DSL {
8
  path?: string[];
9
  answer?: any[];
10
  graph?: IGraph;
 
 
11
  }
12
 
13
  export interface IOperator {
@@ -32,13 +35,7 @@ export interface IFlow {
32
  create_date: string;
33
  create_time: number;
34
  description: null;
35
- dsl: {
36
- answer: any[];
37
- components: DSLComponents;
38
- graph: IGraph;
39
- history: any[];
40
- path: string[];
41
- };
42
  id: string;
43
  title: string;
44
  update_date: string;
 
1
  import { Edge, Node } from 'reactflow';
2
+ import { IReference, Message } from './chat';
3
 
4
  export type DSLComponents = Record<string, IOperator>;
5
 
 
9
  path?: string[];
10
  answer?: any[];
11
  graph?: IGraph;
12
+ messages: Message[];
13
+ reference: IReference[];
14
  }
15
 
16
  export interface IOperator {
 
35
  create_date: string;
36
  create_time: number;
37
  description: null;
38
+ dsl: DSL;
 
 
 
 
 
 
39
  id: string;
40
  title: string;
41
  update_date: string;
web/src/pages/chat/utils.ts CHANGED
@@ -1,7 +1,7 @@
1
  import { MessageType } from '@/constants/chat';
2
  import { IConversation, IReference } from '@/interfaces/database/chat';
3
  import { EmptyConversationId } from './constants';
4
- import { IClientConversation, IMessage } from './interface';
5
 
6
  export const isConversationIdExist = (conversationId: string) => {
7
  return conversationId !== EmptyConversationId && conversationId !== '';
@@ -25,7 +25,7 @@ export const getDocumentIdsFromConversionReference = (data: IConversation) => {
25
  };
26
 
27
  export const buildMessageItemReference = (
28
- conversation: IClientConversation,
29
  message: IMessage,
30
  ) => {
31
  const assistantMessages = conversation.message
 
1
  import { MessageType } from '@/constants/chat';
2
  import { IConversation, IReference } from '@/interfaces/database/chat';
3
  import { EmptyConversationId } from './constants';
4
+ import { IMessage } from './interface';
5
 
6
  export const isConversationIdExist = (conversationId: string) => {
7
  return conversationId !== EmptyConversationId && conversationId !== '';
 
25
  };
26
 
27
  export const buildMessageItemReference = (
28
+ conversation: { message: IMessage[]; reference: IReference[] },
29
  message: IMessage,
30
  ) => {
31
  const assistantMessages = conversation.message
web/src/pages/flow/chat/box.tsx CHANGED
@@ -2,43 +2,34 @@ import MessageItem from '@/components/message-item';
2
  import DocumentPreviewer from '@/components/pdf-previewer';
3
  import { MessageType } from '@/constants/chat';
4
  import { useTranslate } from '@/hooks/commonHooks';
5
- import {
6
- useClickDrawer,
7
- useFetchConversationOnMount,
8
- useGetFileIcon,
9
- useGetSendButtonDisabled,
10
- useSelectConversationLoading,
11
- useSendMessage,
12
- } from '@/pages/chat/hooks';
13
  import { buildMessageItemReference } from '@/pages/chat/utils';
14
  import { Button, Drawer, Flex, Input, Spin } from 'antd';
15
 
 
 
16
  import styles from './index.less';
17
 
18
  const FlowChatBox = () => {
19
  const {
20
  ref,
21
- currentConversation: conversation,
22
- addNewestConversation,
23
- removeLatestMessage,
24
  addNewestAnswer,
25
- } = useFetchConversationOnMount();
 
 
 
 
26
  const {
27
  handleInputChange,
28
  handlePressEnter,
29
  value,
30
  loading: sendLoading,
31
- } = useSendMessage(
32
- conversation,
33
- addNewestConversation,
34
- removeLatestMessage,
35
- addNewestAnswer,
36
- );
37
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
38
  useClickDrawer();
39
- const disabled = useGetSendButtonDisabled();
40
  useGetFileIcon();
41
- const loading = useSelectConversationLoading();
42
  const { t } = useTranslate('chat');
43
 
44
  return (
@@ -47,17 +38,20 @@ const FlowChatBox = () => {
47
  <Flex flex={1} vertical className={styles.messageContainer}>
48
  <div>
49
  <Spin spinning={loading}>
50
- {conversation?.message?.map((message, i) => {
51
  return (
52
  <MessageItem
53
  loading={
54
  message.role === MessageType.Assistant &&
55
  sendLoading &&
56
- conversation?.message.length - 1 === i
57
  }
58
  key={message.id}
59
  item={message}
60
- reference={buildMessageItemReference(conversation, message)}
 
 
 
61
  clickDocumentButton={clickDocumentButton}
62
  ></MessageItem>
63
  );
@@ -70,13 +64,11 @@ const FlowChatBox = () => {
70
  size="large"
71
  placeholder={t('sendPlaceholder')}
72
  value={value}
73
- disabled={disabled}
74
  suffix={
75
  <Button
76
  type="primary"
77
  onClick={handlePressEnter}
78
  loading={sendLoading}
79
- disabled={disabled}
80
  >
81
  {t('send')}
82
  </Button>
 
2
  import DocumentPreviewer from '@/components/pdf-previewer';
3
  import { MessageType } from '@/constants/chat';
4
  import { useTranslate } from '@/hooks/commonHooks';
5
+ import { useClickDrawer, useGetFileIcon } from '@/pages/chat/hooks';
 
 
 
 
 
 
 
6
  import { buildMessageItemReference } from '@/pages/chat/utils';
7
  import { Button, Drawer, Flex, Input, Spin } from 'antd';
8
 
9
+ import { useSelectCurrentMessages, useSendMessage } from './hooks';
10
+
11
  import styles from './index.less';
12
 
13
  const FlowChatBox = () => {
14
  const {
15
  ref,
16
+ currentMessages,
17
+ reference,
 
18
  addNewestAnswer,
19
+ addNewestQuestion,
20
+ removeLatestMessage,
21
+ loading,
22
+ } = useSelectCurrentMessages();
23
+
24
  const {
25
  handleInputChange,
26
  handlePressEnter,
27
  value,
28
  loading: sendLoading,
29
+ } = useSendMessage(addNewestQuestion, removeLatestMessage, addNewestAnswer);
 
 
 
 
 
30
  const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
31
  useClickDrawer();
 
32
  useGetFileIcon();
 
33
  const { t } = useTranslate('chat');
34
 
35
  return (
 
38
  <Flex flex={1} vertical className={styles.messageContainer}>
39
  <div>
40
  <Spin spinning={loading}>
41
+ {currentMessages?.map((message, i) => {
42
  return (
43
  <MessageItem
44
  loading={
45
  message.role === MessageType.Assistant &&
46
  sendLoading &&
47
+ currentMessages.length - 1 === i
48
  }
49
  key={message.id}
50
  item={message}
51
+ reference={buildMessageItemReference(
52
+ { message: currentMessages, reference },
53
+ message,
54
+ )}
55
  clickDocumentButton={clickDocumentButton}
56
  ></MessageItem>
57
  );
 
64
  size="large"
65
  placeholder={t('sendPlaceholder')}
66
  value={value}
 
67
  suffix={
68
  <Button
69
  type="primary"
70
  onClick={handlePressEnter}
71
  loading={sendLoading}
 
72
  >
73
  {t('send')}
74
  </Button>
web/src/pages/flow/chat/drawer.tsx CHANGED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IModalProps } from '@/interfaces/common';
2
+ import { Drawer } from 'antd';
3
+ import FlowChatBox from './box';
4
+
5
+ const ChatDrawer = ({ visible, hideModal }: IModalProps<any>) => {
6
+ return (
7
+ <Drawer
8
+ title="Debug"
9
+ placement="right"
10
+ onClose={hideModal}
11
+ open={visible}
12
+ getContainer={false}
13
+ width={470}
14
+ zIndex={10000}
15
+ >
16
+ <FlowChatBox></FlowChatBox>
17
+ </Drawer>
18
+ );
19
+ };
20
+
21
+ export default ChatDrawer;
web/src/pages/flow/chat/hooks.ts CHANGED
@@ -2,27 +2,25 @@ import { MessageType } from '@/constants/chat';
2
  import { useFetchFlow } from '@/hooks/flow-hooks';
3
  import {
4
  useHandleMessageInputChange,
5
- // useScrollToBottom,
6
  useSendMessageWithSse,
7
  } from '@/hooks/logicHooks';
8
  import { IAnswer } from '@/interfaces/database/chat';
9
  import { IMessage } from '@/pages/chat/interface';
10
- import omit from 'lodash/omit';
11
  import { useCallback, useEffect, useState } from 'react';
12
  import { useParams } from 'umi';
13
  import { v4 as uuid } from 'uuid';
14
- import { Operator } from '../constant';
15
- import useGraphStore from '../store';
16
 
17
- export const useSelectCurrentConversation = () => {
18
  const { id: id } = useParams();
19
- const findNodeByName = useGraphStore((state) => state.findNodeByName);
20
  const [currentMessages, setCurrentMessages] = useState<IMessage[]>([]);
21
 
22
- const { data: flowDetail } = useFetchFlow();
23
- const messages = flowDetail.dsl.history;
 
24
 
25
- const prologue = findNodeByName(Operator.Begin)?.data?.form?.prologue;
26
 
27
  const addNewestQuestion = useCallback(
28
  (message: string, answer: string = '') => {
@@ -45,121 +43,71 @@ export const useSelectCurrentConversation = () => {
45
  [],
46
  );
47
 
48
- const addNewestAnswer = useCallback(
49
- (answer: IAnswer) => {
50
- setCurrentMessages((pre) => {
51
- const latestMessage = currentMessages?.at(-1);
52
-
53
- if (latestMessage) {
54
- return [
55
- ...pre.slice(0, -1),
56
- {
57
- ...latestMessage,
58
- content: answer.answer,
59
- reference: answer.reference,
60
- },
61
- ];
62
- }
63
- return pre;
64
- });
65
- },
66
- [currentMessages],
67
- );
68
 
69
  const removeLatestMessage = useCallback(() => {
70
  setCurrentMessages((pre) => {
71
  const nextMessages = pre?.slice(0, -2) ?? [];
 
72
  return [...pre, ...nextMessages];
73
  });
74
  }, []);
75
 
76
- const addPrologue = useCallback(() => {
77
- if (id === '') {
78
- const nextMessage = {
79
- role: MessageType.Assistant,
80
- content: prologue,
81
- id: uuid(),
82
- } as IMessage;
83
-
84
- setCurrentMessages({
85
- id: '',
86
- reference: [],
87
- message: [nextMessage],
88
- } as any);
89
- }
90
- }, [id, prologue]);
91
-
92
- useEffect(() => {
93
- addPrologue();
94
- }, [addPrologue]);
95
-
96
  useEffect(() => {
97
  if (id) {
98
- setCurrentMessages(messages);
 
99
  }
100
  }, [messages, id]);
101
 
102
  return {
103
- currentConversation: currentMessages,
 
104
  addNewestQuestion,
105
  removeLatestMessage,
106
  addNewestAnswer,
 
 
107
  };
108
  };
109
 
110
- // export const useFetchConversationOnMount = () => {
111
- // const { conversationId } = useGetChatSearchParams();
112
- // const fetchConversation = useFetchConversation();
113
- // const {
114
- // currentConversation,
115
- // addNewestQuestion,
116
- // removeLatestMessage,
117
- // addNewestAnswer,
118
- // } = useSelectCurrentConversation();
119
- // const ref = useScrollToBottom(currentConversation);
120
-
121
- // const fetchConversationOnMount = useCallback(() => {
122
- // if (isConversationIdExist(conversationId)) {
123
- // fetchConversation(conversationId);
124
- // }
125
- // }, [fetchConversation, conversationId]);
126
-
127
- // useEffect(() => {
128
- // fetchConversationOnMount();
129
- // }, [fetchConversationOnMount]);
130
-
131
- // return {
132
- // currentConversation,
133
- // addNewestQuestion,
134
- // ref,
135
- // removeLatestMessage,
136
- // addNewestAnswer,
137
- // };
138
- // };
139
-
140
  export const useSendMessage = (
141
- conversation: any,
142
  addNewestQuestion: (message: string, answer?: string) => void,
143
  removeLatestMessage: () => void,
144
  addNewestAnswer: (answer: IAnswer) => void,
145
  ) => {
146
- const { id: conversationId } = useParams();
147
  const { handleInputChange, value, setValue } = useHandleMessageInputChange();
 
 
148
 
149
- const { send, answer, done } = useSendMessageWithSse();
150
 
151
  const sendMessage = useCallback(
152
  async (message: string, id?: string) => {
153
- const res: Response | undefined = await send({
154
- conversation_id: id ?? conversationId,
155
- messages: [
156
- ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')),
157
- {
158
- role: MessageType.User,
159
- content: message,
160
- },
161
- ],
162
- });
163
 
164
  if (res?.status !== 200) {
165
  // cancel loading
@@ -167,13 +115,7 @@ export const useSendMessage = (
167
  removeLatestMessage();
168
  }
169
  },
170
- [
171
- conversation?.message,
172
- conversationId,
173
- removeLatestMessage,
174
- setValue,
175
- send,
176
- ],
177
  );
178
 
179
  const handleSendMessage = useCallback(
@@ -189,6 +131,13 @@ export const useSendMessage = (
189
  }
190
  }, [answer, addNewestAnswer]);
191
 
 
 
 
 
 
 
 
192
  const handlePressEnter = useCallback(() => {
193
  if (done) {
194
  setValue('');
 
2
  import { useFetchFlow } from '@/hooks/flow-hooks';
3
  import {
4
  useHandleMessageInputChange,
5
+ useScrollToBottom,
6
  useSendMessageWithSse,
7
  } from '@/hooks/logicHooks';
8
  import { IAnswer } from '@/interfaces/database/chat';
9
  import { IMessage } from '@/pages/chat/interface';
10
+ import api from '@/utils/api';
11
  import { useCallback, useEffect, useState } from 'react';
12
  import { useParams } from 'umi';
13
  import { v4 as uuid } from 'uuid';
 
 
14
 
15
+ export const useSelectCurrentMessages = () => {
16
  const { id: id } = useParams();
 
17
  const [currentMessages, setCurrentMessages] = useState<IMessage[]>([]);
18
 
19
+ const { data: flowDetail, loading } = useFetchFlow();
20
+ const messages = flowDetail.dsl.messages;
21
+ const reference = flowDetail.dsl.reference;
22
 
23
+ const ref = useScrollToBottom(currentMessages);
24
 
25
  const addNewestQuestion = useCallback(
26
  (message: string, answer: string = '') => {
 
43
  [],
44
  );
45
 
46
+ const addNewestAnswer = useCallback((answer: IAnswer) => {
47
+ setCurrentMessages((pre) => {
48
+ const latestMessage = pre?.at(-1);
49
+
50
+ if (latestMessage) {
51
+ return [
52
+ ...pre.slice(0, -1),
53
+ {
54
+ ...latestMessage,
55
+ content: answer.answer,
56
+ reference: answer.reference,
57
+ },
58
+ ];
59
+ }
60
+ return pre;
61
+ });
62
+ }, []);
 
 
 
63
 
64
  const removeLatestMessage = useCallback(() => {
65
  setCurrentMessages((pre) => {
66
  const nextMessages = pre?.slice(0, -2) ?? [];
67
+ return nextMessages;
68
  return [...pre, ...nextMessages];
69
  });
70
  }, []);
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  useEffect(() => {
73
  if (id) {
74
+ const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
75
+ setCurrentMessages(nextMessages);
76
  }
77
  }, [messages, id]);
78
 
79
  return {
80
+ currentMessages,
81
+ reference,
82
  addNewestQuestion,
83
  removeLatestMessage,
84
  addNewestAnswer,
85
+ ref,
86
+ loading,
87
  };
88
  };
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  export const useSendMessage = (
 
91
  addNewestQuestion: (message: string, answer?: string) => void,
92
  removeLatestMessage: () => void,
93
  addNewestAnswer: (answer: IAnswer) => void,
94
  ) => {
95
+ const { id: flowId } = useParams();
96
  const { handleInputChange, value, setValue } = useHandleMessageInputChange();
97
+ const { data: flowDetail } = useFetchFlow();
98
+ const messages = flowDetail.dsl.messages;
99
 
100
+ const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
101
 
102
  const sendMessage = useCallback(
103
  async (message: string, id?: string) => {
104
+ const params: Record<string, unknown> = {
105
+ id: flowId,
106
+ };
107
+ if (message) {
108
+ params.message = message;
109
+ }
110
+ const res: Response | undefined = await send(params);
 
 
 
111
 
112
  if (res?.status !== 200) {
113
  // cancel loading
 
115
  removeLatestMessage();
116
  }
117
  },
118
+ [flowId, removeLatestMessage, setValue, send],
 
 
 
 
 
 
119
  );
120
 
121
  const handleSendMessage = useCallback(
 
131
  }
132
  }, [answer, addNewestAnswer]);
133
 
134
+ useEffect(() => {
135
+ // fetch prologue
136
+ if (messages.length === 0) {
137
+ sendMessage('');
138
+ }
139
+ }, [sendMessage, messages]);
140
+
141
  const handlePressEnter = useCallback(() => {
142
  if (done) {
143
  setValue('');
web/src/pages/flow/chat/index.less CHANGED
@@ -1,5 +1,6 @@
1
  .chatContainer {
2
- padding: 0 0 24px 24px;
 
3
  .messageContainer {
4
  overflow-y: auto;
5
  padding-right: 24px;
 
1
  .chatContainer {
2
+ padding: 0;
3
+ height: 100%;
4
  .messageContainer {
5
  overflow-y: auto;
6
  padding-right: 24px;
web/src/pages/flow/header/index.less CHANGED
@@ -1,3 +1,3 @@
1
  .flowHeader {
2
- padding: 20px;
3
  }
 
1
  .flowHeader {
2
+ padding: 0 20px;
3
  }
web/src/pages/flow/header/index.tsx CHANGED
@@ -1,26 +1,51 @@
1
- import { Button, Flex } from 'antd';
2
 
 
 
 
 
 
3
  import { useRunGraph, useSaveGraph } from '../hooks';
4
  import styles from './index.less';
5
 
6
  const FlowHeader = () => {
7
  const { saveGraph } = useSaveGraph();
8
  const { runGraph } = useRunGraph();
 
 
 
 
 
 
9
 
10
  return (
11
- <Flex
12
- align="center"
13
- justify="end"
14
- gap={'large'}
15
- className={styles.flowHeader}
16
- >
17
- <Button onClick={runGraph}>
18
- <b>Debug</b>
19
- </Button>
20
- <Button type="primary" onClick={saveGraph}>
21
- <b>Save</b>
22
- </Button>
23
- </Flex>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  );
25
  };
26
 
 
1
+ import { Button, Flex, Space } from 'antd';
2
 
3
+ import { useSetModalState } from '@/hooks/commonHooks';
4
+ import { useFetchFlow } from '@/hooks/flow-hooks';
5
+ import { ArrowLeftOutlined } from '@ant-design/icons';
6
+ import { Link } from 'umi';
7
+ import ChatDrawer from '../chat/drawer';
8
  import { useRunGraph, useSaveGraph } from '../hooks';
9
  import styles from './index.less';
10
 
11
  const FlowHeader = () => {
12
  const { saveGraph } = useSaveGraph();
13
  const { runGraph } = useRunGraph();
14
+ const {
15
+ visible: chatDrawerVisible,
16
+ hideModal: hideChatDrawer,
17
+ showModal: showChatDrawer,
18
+ } = useSetModalState();
19
+ const { data } = useFetchFlow();
20
 
21
  return (
22
+ <>
23
+ <Flex
24
+ align="center"
25
+ justify={'space-between'}
26
+ gap={'large'}
27
+ className={styles.flowHeader}
28
+ >
29
+ <Space size={'large'}>
30
+ <Link to={`/flow`}>
31
+ <ArrowLeftOutlined />
32
+ </Link>
33
+ <h3>{data.title}</h3>
34
+ </Space>
35
+ <Space size={'large'}>
36
+ <Button onClick={showChatDrawer}>
37
+ <b>Debug</b>
38
+ </Button>
39
+ <Button type="primary" onClick={saveGraph}>
40
+ <b>Save</b>
41
+ </Button>
42
+ </Space>
43
+ </Flex>
44
+ <ChatDrawer
45
+ visible={chatDrawerVisible}
46
+ hideModal={hideChatDrawer}
47
+ ></ChatDrawer>
48
+ </>
49
  );
50
  };
51
 
web/src/pages/flow/hooks.ts CHANGED
@@ -187,7 +187,7 @@ const useSetGraphInfo = () => {
187
  const { setEdges, setNodes } = useGraphStore((state) => state);
188
  const setGraphInfo = useCallback(
189
  ({ nodes = [], edges = [] }: IGraph) => {
190
- if (nodes.length && edges.length) {
191
  setNodes(nodes);
192
  setEdges(edges);
193
  }
@@ -202,7 +202,7 @@ export const useFetchDataOnMount = () => {
202
  const setGraphInfo = useSetGraphInfo();
203
 
204
  useEffect(() => {
205
- setGraphInfo(data?.dsl?.graph ?? {});
206
  }, [setGraphInfo, data?.dsl?.graph]);
207
 
208
  useWatchGraphChange();
 
187
  const { setEdges, setNodes } = useGraphStore((state) => state);
188
  const setGraphInfo = useCallback(
189
  ({ nodes = [], edges = [] }: IGraph) => {
190
+ if (nodes.length || edges.length) {
191
  setNodes(nodes);
192
  setEdges(edges);
193
  }
 
202
  const setGraphInfo = useSetGraphInfo();
203
 
204
  useEffect(() => {
205
+ setGraphInfo(data?.dsl?.graph ?? ({} as IGraph));
206
  }, [setGraphInfo, data?.dsl?.graph]);
207
 
208
  useWatchGraphChange();
web/src/pages/flow/list/flow-card/index.tsx CHANGED
@@ -1,17 +1,12 @@
1
- import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg';
2
- import { useShowDeleteConfirm } from '@/hooks/commonHooks';
3
  import { formatDate } from '@/utils/date';
4
- import {
5
- CalendarOutlined,
6
- DeleteOutlined,
7
- UserOutlined,
8
- } from '@ant-design/icons';
9
- import { Avatar, Card, Dropdown, MenuProps, Space } from 'antd';
10
- import { useTranslation } from 'react-i18next';
11
  import { useNavigate } from 'umi';
12
 
 
13
  import { useDeleteFlow } from '@/hooks/flow-hooks';
14
  import { IFlow } from '@/interfaces/database/flow';
 
15
  import styles from './index.less';
16
 
17
  interface IProps {
@@ -20,37 +15,11 @@ interface IProps {
20
 
21
  const FlowCard = ({ item }: IProps) => {
22
  const navigate = useNavigate();
23
- const showDeleteConfirm = useShowDeleteConfirm();
24
- const { t } = useTranslation();
25
  const { deleteFlow } = useDeleteFlow();
26
 
27
- const removeKnowledge = () => {
28
  return deleteFlow([item.id]);
29
- };
30
-
31
- const handleDelete = () => {
32
- showDeleteConfirm({ onOk: removeKnowledge });
33
- };
34
-
35
- const items: MenuProps['items'] = [
36
- {
37
- key: '1',
38
- label: (
39
- <Space>
40
- {t('common.delete')}
41
- <DeleteOutlined />
42
- </Space>
43
- ),
44
- },
45
- ];
46
-
47
- const handleDropdownMenuClick: MenuProps['onClick'] = ({ domEvent, key }) => {
48
- domEvent.preventDefault();
49
- domEvent.stopPropagation();
50
- if (key === '1') {
51
- handleDelete();
52
- }
53
- };
54
 
55
  const handleCardClick = () => {
56
  navigate(`/flow/${item.id}`);
@@ -61,16 +30,7 @@ const FlowCard = ({ item }: IProps) => {
61
  <div className={styles.container}>
62
  <div className={styles.content}>
63
  <Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
64
- <Dropdown
65
- menu={{
66
- items,
67
- onClick: handleDropdownMenuClick,
68
- }}
69
- >
70
- <span className={styles.delete}>
71
- <MoreIcon />
72
- </span>
73
- </Dropdown>
74
  </div>
75
  <div className={styles.titleWrapper}>
76
  <span className={styles.title}>{item.title}</span>
 
 
 
1
  import { formatDate } from '@/utils/date';
2
+ import { CalendarOutlined, UserOutlined } from '@ant-design/icons';
3
+ import { Avatar, Card } from 'antd';
 
 
 
 
 
4
  import { useNavigate } from 'umi';
5
 
6
+ import OperateDropdown from '@/components/operate-dropdown';
7
  import { useDeleteFlow } from '@/hooks/flow-hooks';
8
  import { IFlow } from '@/interfaces/database/flow';
9
+ import { useCallback } from 'react';
10
  import styles from './index.less';
11
 
12
  interface IProps {
 
15
 
16
  const FlowCard = ({ item }: IProps) => {
17
  const navigate = useNavigate();
 
 
18
  const { deleteFlow } = useDeleteFlow();
19
 
20
+ const removeFlow = useCallback(() => {
21
  return deleteFlow([item.id]);
22
+ }, [deleteFlow, item]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  const handleCardClick = () => {
25
  navigate(`/flow/${item.id}`);
 
30
  <div className={styles.container}>
31
  <div className={styles.content}>
32
  <Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
33
+ <OperateDropdown deleteItem={removeFlow}></OperateDropdown>
 
 
 
 
 
 
 
 
 
34
  </div>
35
  <div className={styles.titleWrapper}>
36
  <span className={styles.title}>{item.title}</span>
web/src/pages/flow/list/hooks.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { useSetModalState } from '@/hooks/commonHooks';
2
  import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
3
  import { useCallback, useState } from 'react';
 
4
  import { dsl } from '../mock';
5
 
6
  export const useFetchDataOnMount = () => {
@@ -17,16 +18,18 @@ export const useSaveFlow = () => {
17
  showModal: showFileRenameModal,
18
  } = useSetModalState();
19
  const { loading, setFlow } = useSetFlow();
 
20
 
21
  const onFlowOk = useCallback(
22
  async (title: string) => {
23
  const ret = await setFlow({ title, dsl });
24
 
25
- if (ret === 0) {
26
  hideFlowSettingModal();
 
27
  }
28
  },
29
- [setFlow, hideFlowSettingModal],
30
  );
31
 
32
  const handleShowFlowSettingModal = useCallback(
 
1
  import { useSetModalState } from '@/hooks/commonHooks';
2
  import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
3
  import { useCallback, useState } from 'react';
4
+ import { useNavigate } from 'umi';
5
  import { dsl } from '../mock';
6
 
7
  export const useFetchDataOnMount = () => {
 
18
  showModal: showFileRenameModal,
19
  } = useSetModalState();
20
  const { loading, setFlow } = useSetFlow();
21
+ const navigate = useNavigate();
22
 
23
  const onFlowOk = useCallback(
24
  async (title: string) => {
25
  const ret = await setFlow({ title, dsl });
26
 
27
+ if (ret?.retcode === 0) {
28
  hideFlowSettingModal();
29
+ navigate(`/flow/${ret.data.id}`);
30
  }
31
  },
32
+ [setFlow, hideFlowSettingModal, navigate],
33
  );
34
 
35
  const handleShowFlowSettingModal = useCallback(
web/src/pages/flow/mock.tsx CHANGED
@@ -49,83 +49,83 @@ export const dsl = {
49
  sourcePosition: 'left',
50
  targetPosition: 'right',
51
  },
52
- {
53
- id: 'Answer:China',
54
- type: 'textUpdater',
55
- position: {
56
- x: 150,
57
- y: 200,
58
- },
59
- data: {
60
- label: 'Answer',
61
- },
62
- sourcePosition: 'left',
63
- targetPosition: 'right',
64
- },
65
- {
66
- id: 'Retrieval:China',
67
- type: 'textUpdater',
68
- position: {
69
- x: 250,
70
- y: 200,
71
- },
72
- data: {
73
- label: 'Retrieval',
74
- },
75
- sourcePosition: 'left',
76
- targetPosition: 'right',
77
- },
78
- {
79
- id: 'Generate:China',
80
- type: 'textUpdater',
81
- position: {
82
- x: 100,
83
- y: 100,
84
- },
85
- data: {
86
- label: 'Generate',
87
- },
88
- sourcePosition: 'left',
89
- targetPosition: 'right',
90
- },
91
  ],
92
  edges: [
93
- {
94
- id: '7facb53d-65c9-43b3-ac55-339c445d3891',
95
- label: '',
96
- source: 'begin',
97
- target: 'Answer:China',
98
- markerEnd: {
99
- type: 'arrow',
100
- },
101
- },
102
- {
103
- id: '7ac83631-502d-410f-a6e7-bec6866a5e99',
104
- label: '',
105
- source: 'Generate:China',
106
- target: 'Answer:China',
107
- markerEnd: {
108
- type: 'arrow',
109
- },
110
- },
111
- {
112
- id: '0aaab297-5779-43ed-9281-2c4d3741566f',
113
- label: '',
114
- source: 'Answer:China',
115
- target: 'Retrieval:China',
116
- markerEnd: {
117
- type: 'arrow',
118
- },
119
- },
120
- {
121
- id: '3477f9f3-0a7d-400e-af96-a11ea7673183',
122
- label: '',
123
- source: 'Retrieval:China',
124
- target: 'Generate:China',
125
- markerEnd: {
126
- type: 'arrow',
127
- },
128
- },
129
  ],
130
  },
131
  components: {
@@ -137,43 +137,45 @@ export const dsl = {
137
  downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id
138
  upstream: [], // edge source is upstream, edge target is current node id
139
  },
140
- 'Answer:China': {
141
- obj: {
142
- component_name: 'Answer',
143
- params: {},
144
- },
145
- downstream: ['Retrieval:China'],
146
- upstream: ['begin', 'Generate:China'],
147
- },
148
- 'Retrieval:China': {
149
- obj: {
150
- component_name: 'Retrieval',
151
- params: {
152
- similarity_threshold: 0.2,
153
- keywords_similarity_weight: 0.3,
154
- top_n: 6,
155
- top_k: 1024,
156
- rerank_id: 'BAAI/bge-reranker-v2-m3',
157
- kb_ids: ['568aa82603b611efa9d9fa163e197198'],
158
- },
159
- },
160
- downstream: ['Generate:China'],
161
- upstream: ['Answer:China'],
162
- },
163
- 'Generate:China': {
164
- obj: {
165
- component_name: 'Generate',
166
- params: {
167
- llm_id: 'deepseek-chat',
168
- prompt:
169
- 'You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence "The answer you are looking for is not found in the knowledge base!" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.',
170
- temperature: 0.2,
171
- },
172
- },
173
- downstream: ['Answer:China'],
174
- upstream: ['Retrieval:China'],
175
- },
176
  },
 
 
177
  history: [],
178
  path: [],
179
  answer: [],
 
49
  sourcePosition: 'left',
50
  targetPosition: 'right',
51
  },
52
+ // {
53
+ // id: 'Answer:China',
54
+ // type: 'textUpdater',
55
+ // position: {
56
+ // x: 150,
57
+ // y: 200,
58
+ // },
59
+ // data: {
60
+ // label: 'Answer',
61
+ // },
62
+ // sourcePosition: 'left',
63
+ // targetPosition: 'right',
64
+ // },
65
+ // {
66
+ // id: 'Retrieval:China',
67
+ // type: 'textUpdater',
68
+ // position: {
69
+ // x: 250,
70
+ // y: 200,
71
+ // },
72
+ // data: {
73
+ // label: 'Retrieval',
74
+ // },
75
+ // sourcePosition: 'left',
76
+ // targetPosition: 'right',
77
+ // },
78
+ // {
79
+ // id: 'Generate:China',
80
+ // type: 'textUpdater',
81
+ // position: {
82
+ // x: 100,
83
+ // y: 100,
84
+ // },
85
+ // data: {
86
+ // label: 'Generate',
87
+ // },
88
+ // sourcePosition: 'left',
89
+ // targetPosition: 'right',
90
+ // },
91
  ],
92
  edges: [
93
+ // {
94
+ // id: '7facb53d-65c9-43b3-ac55-339c445d3891',
95
+ // label: '',
96
+ // source: 'begin',
97
+ // target: 'Answer:China',
98
+ // markerEnd: {
99
+ // type: 'arrow',
100
+ // },
101
+ // },
102
+ // {
103
+ // id: '7ac83631-502d-410f-a6e7-bec6866a5e99',
104
+ // label: '',
105
+ // source: 'Generate:China',
106
+ // target: 'Answer:China',
107
+ // markerEnd: {
108
+ // type: 'arrow',
109
+ // },
110
+ // },
111
+ // {
112
+ // id: '0aaab297-5779-43ed-9281-2c4d3741566f',
113
+ // label: '',
114
+ // source: 'Answer:China',
115
+ // target: 'Retrieval:China',
116
+ // markerEnd: {
117
+ // type: 'arrow',
118
+ // },
119
+ // },
120
+ // {
121
+ // id: '3477f9f3-0a7d-400e-af96-a11ea7673183',
122
+ // label: '',
123
+ // source: 'Retrieval:China',
124
+ // target: 'Generate:China',
125
+ // markerEnd: {
126
+ // type: 'arrow',
127
+ // },
128
+ // },
129
  ],
130
  },
131
  components: {
 
137
  downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id
138
  upstream: [], // edge source is upstream, edge target is current node id
139
  },
140
+ // 'Answer:China': {
141
+ // obj: {
142
+ // component_name: 'Answer',
143
+ // params: {},
144
+ // },
145
+ // downstream: ['Retrieval:China'],
146
+ // upstream: ['begin', 'Generate:China'],
147
+ // },
148
+ // 'Retrieval:China': {
149
+ // obj: {
150
+ // component_name: 'Retrieval',
151
+ // params: {
152
+ // similarity_threshold: 0.2,
153
+ // keywords_similarity_weight: 0.3,
154
+ // top_n: 6,
155
+ // top_k: 1024,
156
+ // rerank_id: 'BAAI/bge-reranker-v2-m3',
157
+ // kb_ids: ['568aa82603b611efa9d9fa163e197198'],
158
+ // },
159
+ // },
160
+ // downstream: ['Generate:China'],
161
+ // upstream: ['Answer:China'],
162
+ // },
163
+ // 'Generate:China': {
164
+ // obj: {
165
+ // component_name: 'Generate',
166
+ // params: {
167
+ // llm_id: 'deepseek-chat',
168
+ // prompt:
169
+ // 'You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence "The answer you are looking for is not found in the knowledge base!" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.',
170
+ // temperature: 0.2,
171
+ // },
172
+ // },
173
+ // downstream: ['Answer:China'],
174
+ // upstream: ['Retrieval:China'],
175
+ // },
176
  },
177
+ messages: [],
178
+ reference: [],
179
  history: [],
180
  path: [],
181
  answer: [],
web/src/pages/flow/utils.ts CHANGED
@@ -147,11 +147,6 @@ export const buildDslComponentsByGraph = (
147
  components[id] = {
148
  obj: {
149
  component_name: operatorName,
150
- // params:
151
- // removeUselessDataInTheOperator(
152
- // operatorName,
153
- // x.data.form as Record<string, unknown>,
154
- // ) ?? {},
155
  params:
156
  buildOperatorParams(operatorName)(
157
  x.data.form as Record<string, unknown>,
 
147
  components[id] = {
148
  obj: {
149
  component_name: operatorName,
 
 
 
 
 
150
  params:
151
  buildOperatorParams(operatorName)(
152
  x.data.form as Record<string, unknown>,
web/src/pages/knowledge/knowledge-card/index.tsx CHANGED
@@ -1,18 +1,16 @@
1
- import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg';
2
  import { KnowledgeRouteKey } from '@/constants/knowledge';
3
- import { useShowDeleteConfirm } from '@/hooks/commonHooks';
4
  import { IKnowledge } from '@/interfaces/database/knowledge';
5
  import { formatDate } from '@/utils/date';
6
  import {
7
  CalendarOutlined,
8
- DeleteOutlined,
9
  FileTextOutlined,
10
  UserOutlined,
11
  } from '@ant-design/icons';
12
- import { Avatar, Card, Dropdown, MenuProps, Space } from 'antd';
13
  import { useTranslation } from 'react-i18next';
14
  import { useDispatch, useNavigate } from 'umi';
15
 
 
16
  import styles from './index.less';
17
 
18
  interface IProps {
@@ -22,10 +20,9 @@ interface IProps {
22
  const KnowledgeCard = ({ item }: IProps) => {
23
  const navigate = useNavigate();
24
  const dispatch = useDispatch();
25
- const showDeleteConfirm = useShowDeleteConfirm();
26
  const { t } = useTranslation();
27
 
28
- const removeKnowledge = () => {
29
  return dispatch({
30
  type: 'knowledgeModel/rmKb',
31
  payload: {
@@ -34,30 +31,6 @@ const KnowledgeCard = ({ item }: IProps) => {
34
  });
35
  };
36
 
37
- const handleDelete = () => {
38
- showDeleteConfirm({ onOk: removeKnowledge });
39
- };
40
-
41
- const items: MenuProps['items'] = [
42
- {
43
- key: '1',
44
- label: (
45
- <Space>
46
- {t('common.delete')}
47
- <DeleteOutlined />
48
- </Space>
49
- ),
50
- },
51
- ];
52
-
53
- const handleDropdownMenuClick: MenuProps['onClick'] = ({ domEvent, key }) => {
54
- domEvent.preventDefault();
55
- domEvent.stopPropagation();
56
- if (key === '1') {
57
- handleDelete();
58
- }
59
- };
60
-
61
  const handleCardClick = () => {
62
  navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${item.id}`, {
63
  state: { from: 'list' },
@@ -69,16 +42,7 @@ const KnowledgeCard = ({ item }: IProps) => {
69
  <div className={styles.container}>
70
  <div className={styles.content}>
71
  <Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
72
- <Dropdown
73
- menu={{
74
- items,
75
- onClick: handleDropdownMenuClick,
76
- }}
77
- >
78
- <span className={styles.delete}>
79
- <MoreIcon />
80
- </span>
81
- </Dropdown>
82
  </div>
83
  <div className={styles.titleWrapper}>
84
  <span className={styles.title}>{item.name}</span>
 
 
1
  import { KnowledgeRouteKey } from '@/constants/knowledge';
 
2
  import { IKnowledge } from '@/interfaces/database/knowledge';
3
  import { formatDate } from '@/utils/date';
4
  import {
5
  CalendarOutlined,
 
6
  FileTextOutlined,
7
  UserOutlined,
8
  } from '@ant-design/icons';
9
+ import { Avatar, Card, Space } from 'antd';
10
  import { useTranslation } from 'react-i18next';
11
  import { useDispatch, useNavigate } from 'umi';
12
 
13
+ import OperateDropdown from '@/components/operate-dropdown';
14
  import styles from './index.less';
15
 
16
  interface IProps {
 
20
  const KnowledgeCard = ({ item }: IProps) => {
21
  const navigate = useNavigate();
22
  const dispatch = useDispatch();
 
23
  const { t } = useTranslation();
24
 
25
+ const removeKnowledge = async () => {
26
  return dispatch({
27
  type: 'knowledgeModel/rmKb',
28
  payload: {
 
31
  });
32
  };
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  const handleCardClick = () => {
35
  navigate(`/knowledge/${KnowledgeRouteKey.Dataset}?id=${item.id}`, {
36
  state: { from: 'list' },
 
42
  <div className={styles.container}>
43
  <div className={styles.content}>
44
  <Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
45
+ <OperateDropdown deleteItem={removeKnowledge}></OperateDropdown>
 
 
 
 
 
 
 
 
 
46
  </div>
47
  <div className={styles.titleWrapper}>
48
  <span className={styles.title}>{item.name}</span>
web/src/utils/api.ts CHANGED
@@ -89,5 +89,5 @@ export default {
89
  removeCanvas: `${api_host}/canvas/rm`,
90
  setCanvas: `${api_host}/canvas/set`,
91
  resetCanvas: `${api_host}/canvas/reset`,
92
- runCanvas: `${api_host}/canvas/run`,
93
  };
 
89
  removeCanvas: `${api_host}/canvas/rm`,
90
  setCanvas: `${api_host}/canvas/set`,
91
  resetCanvas: `${api_host}/canvas/reset`,
92
+ runCanvas: `${api_host}/canvas/completion`,
93
  };