balibabu commited on
Commit
5743e5f
Β·
1 Parent(s): 4a78f27

feat: Expose the agent's chat window to third parties #1842 (#1897)

Browse files

### What problem does this PR solve?

feat: Expose the agent's chat window to third parties #1842
### Type of change


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

web/src/{pages/chat β†’ components/api-service}/chat-api-key-modal/index.tsx RENAMED
@@ -9,12 +9,12 @@ import { Button, Modal, Space, Table } from 'antd';
9
  import { useOperateApiKey } from '../hooks';
10
 
11
  const ChatApiKeyModal = ({
12
- visible,
13
  dialogId,
14
  hideModal,
15
- }: IModalProps<any> & { dialogId: string }) => {
 
16
  const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
17
- useOperateApiKey(visible, dialogId);
18
  const { t } = useTranslate('chat');
19
 
20
  const columns: TableProps<IToken>['columns'] = [
@@ -48,7 +48,7 @@ const ChatApiKeyModal = ({
48
  <>
49
  <Modal
50
  title={t('apiKey')}
51
- open={visible}
52
  onCancel={hideModal}
53
  cancelButtonProps={{ style: { display: 'none' } }}
54
  style={{ top: 300 }}
 
9
  import { useOperateApiKey } from '../hooks';
10
 
11
  const ChatApiKeyModal = ({
 
12
  dialogId,
13
  hideModal,
14
+ idKey,
15
+ }: IModalProps<any> & { dialogId: string; idKey: string }) => {
16
  const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
17
+ useOperateApiKey(dialogId, idKey);
18
  const { t } = useTranslate('chat');
19
 
20
  const columns: TableProps<IToken>['columns'] = [
 
48
  <>
49
  <Modal
50
  title={t('apiKey')}
51
+ open
52
  onCancel={hideModal}
53
  cancelButtonProps={{ style: { display: 'none' } }}
54
  style={{ top: 300 }}
web/src/{pages/chat β†’ components/api-service}/chat-overview-modal/index.less RENAMED
File without changes
web/src/{pages/chat β†’ components/api-service}/chat-overview-modal/index.tsx RENAMED
@@ -1,7 +1,8 @@
1
  import LineChart from '@/components/line-chart';
 
2
  import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
3
  import { IModalProps } from '@/interfaces/common';
4
- import { IDialog, IStats } from '@/interfaces/database/chat';
5
  import { formatDate } from '@/utils/date';
6
  import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
7
  import { RangePickerProps } from 'antd/es/date-picker';
@@ -10,7 +11,6 @@ import camelCase from 'lodash/camelCase';
10
  import ChatApiKeyModal from '../chat-api-key-modal';
11
  import EmbedModal from '../embed-modal';
12
  import {
13
- useFetchStatsOnMount,
14
  usePreviewChat,
15
  useSelectChartStatsList,
16
  useShowEmbedModal,
@@ -40,8 +40,10 @@ const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
40
  const ChatOverviewModal = ({
41
  visible,
42
  hideModal,
43
- dialog,
44
- }: IModalProps<any> & { dialog: IDialog }) => {
 
 
45
  const { t } = useTranslate('chat');
46
  const {
47
  visible: apiKeyVisible,
@@ -54,15 +56,15 @@ const ChatOverviewModal = ({
54
  showEmbedModal,
55
  embedToken,
56
  errorContextHolder,
57
- } = useShowEmbedModal(dialog.id);
58
 
59
- const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
60
 
61
  const disabledDate: RangePickerProps['disabledDate'] = (current) => {
62
  return current && current > dayjs().endOf('day');
63
  };
64
 
65
- const { handlePreview, contextHolder } = usePreviewChat(dialog.id);
66
 
67
  return (
68
  <>
@@ -97,7 +99,7 @@ const ChatOverviewModal = ({
97
  </a>
98
  </Space>
99
  </Card>
100
- <Card title={`${dialog.name} Web App`}>
101
  <Flex gap={8} vertical>
102
  <Space size={'middle'}>
103
  <Button onClick={handlePreview}>{t('preview')}</Button>
@@ -124,11 +126,13 @@ const ChatOverviewModal = ({
124
  <StatsLineChart statsType={'uv'}></StatsLineChart>
125
  </div>
126
  </Flex>
127
- <ChatApiKeyModal
128
- visible={apiKeyVisible}
129
- hideModal={hideApiKeyModal}
130
- dialogId={dialog.id}
131
- ></ChatApiKeyModal>
 
 
132
  <EmbedModal
133
  token={embedToken}
134
  visible={embedVisible}
 
1
  import LineChart from '@/components/line-chart';
2
+ import { useFetchNextStats } from '@/hooks/chat-hooks';
3
  import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
4
  import { IModalProps } from '@/interfaces/common';
5
+ import { IStats } from '@/interfaces/database/chat';
6
  import { formatDate } from '@/utils/date';
7
  import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
8
  import { RangePickerProps } from 'antd/es/date-picker';
 
11
  import ChatApiKeyModal from '../chat-api-key-modal';
12
  import EmbedModal from '../embed-modal';
13
  import {
 
14
  usePreviewChat,
15
  useSelectChartStatsList,
16
  useShowEmbedModal,
 
40
  const ChatOverviewModal = ({
41
  visible,
42
  hideModal,
43
+ id,
44
+ name = '',
45
+ idKey,
46
+ }: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
47
  const { t } = useTranslate('chat');
48
  const {
49
  visible: apiKeyVisible,
 
56
  showEmbedModal,
57
  embedToken,
58
  errorContextHolder,
59
+ } = useShowEmbedModal(id, idKey);
60
 
61
+ const { pickerValue, setPickerValue } = useFetchNextStats();
62
 
63
  const disabledDate: RangePickerProps['disabledDate'] = (current) => {
64
  return current && current > dayjs().endOf('day');
65
  };
66
 
67
+ const { handlePreview, contextHolder } = usePreviewChat(id, idKey);
68
 
69
  return (
70
  <>
 
99
  </a>
100
  </Space>
101
  </Card>
102
+ <Card title={`${name} Web App`}>
103
  <Flex gap={8} vertical>
104
  <Space size={'middle'}>
105
  <Button onClick={handlePreview}>{t('preview')}</Button>
 
126
  <StatsLineChart statsType={'uv'}></StatsLineChart>
127
  </div>
128
  </Flex>
129
+ {apiKeyVisible && (
130
+ <ChatApiKeyModal
131
+ hideModal={hideApiKeyModal}
132
+ dialogId={id}
133
+ idKey={idKey}
134
+ ></ChatApiKeyModal>
135
+ )}
136
  <EmbedModal
137
  token={embedToken}
138
  visible={embedVisible}
web/src/{pages/chat β†’ components/api-service}/embed-modal/index.less RENAMED
File without changes
web/src/{pages/chat β†’ components/api-service}/embed-modal/index.tsx RENAMED
File without changes
web/src/components/api-service/hooks.ts ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ useCreateNextToken,
3
+ useFetchNextStats,
4
+ useFetchTokenList,
5
+ useRemoveNextToken,
6
+ } from '@/hooks/chat-hooks';
7
+ import {
8
+ useSetModalState,
9
+ useShowDeleteConfirm,
10
+ useTranslate,
11
+ } from '@/hooks/common-hooks';
12
+ import { IStats } from '@/interfaces/database/chat';
13
+ import { message } from 'antd';
14
+ import { useCallback } from 'react';
15
+
16
+ export const useOperateApiKey = (dialogId: string, idKey: string) => {
17
+ const { removeToken } = useRemoveNextToken();
18
+ const { createToken, loading: creatingLoading } = useCreateNextToken();
19
+ const { data: tokenList, loading: listLoading } = useFetchTokenList({
20
+ [idKey]: dialogId,
21
+ });
22
+
23
+ const showDeleteConfirm = useShowDeleteConfirm();
24
+
25
+ const onRemoveToken = (token: string, tenantId: string) => {
26
+ showDeleteConfirm({
27
+ onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
28
+ });
29
+ };
30
+
31
+ const onCreateToken = useCallback(() => {
32
+ createToken({ [idKey]: dialogId });
33
+ }, [createToken, idKey, dialogId]);
34
+
35
+ return {
36
+ removeToken: onRemoveToken,
37
+ createToken: onCreateToken,
38
+ tokenList,
39
+ creatingLoading,
40
+ listLoading,
41
+ };
42
+ };
43
+
44
+ type ChartStatsType = {
45
+ [k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
46
+ };
47
+
48
+ export const useSelectChartStatsList = (): ChartStatsType => {
49
+ const { data: stats } = useFetchNextStats();
50
+
51
+ return Object.keys(stats).reduce((pre, cur) => {
52
+ const item = stats[cur as keyof IStats];
53
+ if (item.length > 0) {
54
+ pre[cur as keyof IStats] = item.map((x) => ({
55
+ xAxis: x[0] as string,
56
+ yAxis: x[1] as number,
57
+ }));
58
+ }
59
+ return pre;
60
+ }, {} as ChartStatsType);
61
+ };
62
+
63
+ export const useShowTokenEmptyError = () => {
64
+ const [messageApi, contextHolder] = message.useMessage();
65
+ const { t } = useTranslate('chat');
66
+
67
+ const showTokenEmptyError = useCallback(() => {
68
+ messageApi.error(t('tokenError'));
69
+ }, [messageApi, t]);
70
+ return { showTokenEmptyError, contextHolder };
71
+ };
72
+
73
+ const getUrlWithToken = (token: string) => {
74
+ const { protocol, host } = window.location;
75
+ return `${protocol}//${host}/chat/share?shared_id=${token}`;
76
+ };
77
+
78
+ const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
79
+ const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
80
+
81
+ const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });
82
+
83
+ const token =
84
+ Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
85
+
86
+ const handleOperate = useCallback(async () => {
87
+ const ret = await refetch();
88
+ const list = ret.data;
89
+ if (Array.isArray(list) && list.length > 0) {
90
+ return list[0]?.token;
91
+ } else {
92
+ showTokenEmptyError();
93
+ return false;
94
+ }
95
+ }, [showTokenEmptyError, refetch]);
96
+
97
+ return {
98
+ token,
99
+ contextHolder,
100
+ handleOperate,
101
+ };
102
+ };
103
+
104
+ export const useShowEmbedModal = (dialogId: string, idKey: string) => {
105
+ const {
106
+ visible: embedVisible,
107
+ hideModal: hideEmbedModal,
108
+ showModal: showEmbedModal,
109
+ } = useSetModalState();
110
+
111
+ const { handleOperate, token, contextHolder } =
112
+ useFetchTokenListBeforeOtherStep(dialogId, idKey);
113
+
114
+ const handleShowEmbedModal = useCallback(async () => {
115
+ const succeed = await handleOperate();
116
+ if (succeed) {
117
+ showEmbedModal();
118
+ }
119
+ }, [handleOperate, showEmbedModal]);
120
+
121
+ return {
122
+ showEmbedModal: handleShowEmbedModal,
123
+ hideEmbedModal,
124
+ embedVisible,
125
+ embedToken: token,
126
+ errorContextHolder: contextHolder,
127
+ };
128
+ };
129
+
130
+ export const usePreviewChat = (dialogId: string, idKey: string) => {
131
+ const { handleOperate, contextHolder } = useFetchTokenListBeforeOtherStep(
132
+ dialogId,
133
+ idKey,
134
+ );
135
+
136
+ const open = useCallback((t: string) => {
137
+ window.open(getUrlWithToken(t), '_blank');
138
+ }, []);
139
+
140
+ const handlePreview = useCallback(async () => {
141
+ const token = await handleOperate();
142
+ if (token) {
143
+ open(token);
144
+ }
145
+ }, [handleOperate, open]);
146
+
147
+ return {
148
+ handlePreview,
149
+ contextHolder,
150
+ };
151
+ };
web/src/hooks/chat-hooks.ts CHANGED
@@ -4,7 +4,10 @@ import {
4
  IStats,
5
  IToken,
6
  } from '@/interfaces/database/chat';
7
- import { useCallback } from 'react';
 
 
 
8
  import { useDispatch, useSelector } from 'umi';
9
 
10
  export const useFetchDialogList = () => {
@@ -175,79 +178,108 @@ export const useCompleteConversation = () => {
175
 
176
  // #region API provided for external calls
177
 
178
- export const useCreateToken = (dialogId: string) => {
179
  const dispatch = useDispatch();
180
 
181
  const createToken = useCallback(() => {
182
  return dispatch<any>({
183
  type: 'chatModel/createToken',
184
- payload: { dialogId },
185
  });
186
- }, [dispatch, dialogId]);
187
 
188
  return createToken;
189
  };
190
 
191
- export const useListToken = () => {
192
- const dispatch = useDispatch();
193
-
194
- const listToken = useCallback(
195
- (dialogId: string) => {
196
- return dispatch<any>({
197
- type: 'chatModel/listToken',
198
- payload: { dialogId },
199
- });
 
 
 
 
 
200
  },
201
- [dispatch],
202
- );
203
 
204
- return listToken;
205
  };
206
 
207
- export const useSelectTokenList = () => {
208
- const tokenList: IToken[] = useSelector(
209
- (state: any) => state.chatModel.tokenList,
210
- );
211
-
212
- return tokenList;
213
- };
214
-
215
- export const useRemoveToken = () => {
216
- const dispatch = useDispatch();
217
-
218
- const removeToken = useCallback(
219
- (payload: { tenantId: string; dialogId: string; tokens: string[] }) => {
220
- return dispatch<any>({
221
- type: 'chatModel/removeToken',
222
- payload: payload,
223
- });
224
  },
225
- [dispatch],
226
- );
227
 
228
- return removeToken;
229
  };
230
 
231
- export const useFetchStats = () => {
232
- const dispatch = useDispatch();
233
-
234
- const fetchStats = useCallback(
235
- (payload: any) => {
236
- return dispatch<any>({
237
- type: 'chatModel/getStats',
238
- payload,
239
- });
 
 
 
 
 
 
 
 
 
240
  },
241
- [dispatch],
242
- );
243
 
244
- return fetchStats;
245
  };
246
 
247
- export const useSelectStats = () => {
248
- const stats: IStats = useSelector((state: any) => state.chatModel.stats);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
- return stats;
251
  };
252
 
253
  //#endregion
 
4
  IStats,
5
  IToken,
6
  } from '@/interfaces/database/chat';
7
+ import chatService from '@/services/chat-service';
8
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
9
+ import dayjs, { Dayjs } from 'dayjs';
10
+ import { useCallback, useState } from 'react';
11
  import { useDispatch, useSelector } from 'umi';
12
 
13
  export const useFetchDialogList = () => {
 
178
 
179
  // #region API provided for external calls
180
 
181
+ export const useCreateToken = (params: Record<string, any>) => {
182
  const dispatch = useDispatch();
183
 
184
  const createToken = useCallback(() => {
185
  return dispatch<any>({
186
  type: 'chatModel/createToken',
187
+ payload: params,
188
  });
189
+ }, [dispatch, params]);
190
 
191
  return createToken;
192
  };
193
 
194
+ export const useCreateNextToken = () => {
195
+ const queryClient = useQueryClient();
196
+ const {
197
+ data,
198
+ isPending: loading,
199
+ mutateAsync,
200
+ } = useMutation({
201
+ mutationKey: ['createToken'],
202
+ mutationFn: async (params: Record<string, any>) => {
203
+ const { data } = await chatService.createToken(params);
204
+ if (data.retcode === 0) {
205
+ queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
206
+ }
207
+ return data?.data ?? [];
208
  },
209
+ });
 
210
 
211
+ return { data, loading, createToken: mutateAsync };
212
  };
213
 
214
+ export const useFetchTokenList = (params: Record<string, any>) => {
215
+ const {
216
+ data,
217
+ isFetching: loading,
218
+ refetch,
219
+ } = useQuery<IToken[]>({
220
+ queryKey: ['fetchTokenList', params],
221
+ initialData: [],
222
+ gcTime: 0,
223
+ queryFn: async () => {
224
+ const { data } = await chatService.listToken(params);
225
+
226
+ return data?.data ?? [];
 
 
 
 
227
  },
228
+ });
 
229
 
230
+ return { data, loading, refetch };
231
  };
232
 
233
+ export const useRemoveNextToken = () => {
234
+ const queryClient = useQueryClient();
235
+ const {
236
+ data,
237
+ isPending: loading,
238
+ mutateAsync,
239
+ } = useMutation({
240
+ mutationKey: ['removeToken'],
241
+ mutationFn: async (params: {
242
+ tenantId: string;
243
+ dialogId: string;
244
+ tokens: string[];
245
+ }) => {
246
+ const { data } = await chatService.removeToken(params);
247
+ if (data.retcode === 0) {
248
+ queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
249
+ }
250
+ return data?.data ?? [];
251
  },
252
+ });
 
253
 
254
+ return { data, loading, removeToken: mutateAsync };
255
  };
256
 
257
+ type RangeValue = [Dayjs | null, Dayjs | null] | null;
258
+
259
+ const getDay = (date?: Dayjs) => date?.format('YYYY-MM-DD');
260
+
261
+ export const useFetchNextStats = () => {
262
+ const [pickerValue, setPickerValue] = useState<RangeValue>([
263
+ dayjs(),
264
+ dayjs().subtract(7, 'day'),
265
+ ]);
266
+ const { data, isFetching: loading } = useQuery<IStats>({
267
+ queryKey: ['fetchStats', pickerValue],
268
+ initialData: {} as IStats,
269
+ gcTime: 0,
270
+ queryFn: async () => {
271
+ if (Array.isArray(pickerValue) && pickerValue[0]) {
272
+ const { data } = await chatService.getStats({
273
+ fromDate: getDay(pickerValue[0]),
274
+ toDate: getDay(pickerValue[1] ?? dayjs()),
275
+ });
276
+ return data?.data ?? {};
277
+ }
278
+ return {};
279
+ },
280
+ });
281
 
282
+ return { data, loading, pickerValue, setPickerValue };
283
  };
284
 
285
  //#endregion
web/src/locales/en.ts CHANGED
@@ -783,6 +783,7 @@ The above is the content you need to summarize.`,
783
  '15d': '12 days',
784
  '30d': '30 days',
785
  },
 
786
  },
787
  footer: {
788
  profile: 'All rights reserved @ React',
 
783
  '15d': '12 days',
784
  '30d': '30 days',
785
  },
786
+ publish: 'Publish',
787
  },
788
  footer: {
789
  profile: 'All rights reserved @ React',
web/src/locales/zh-traditional.ts CHANGED
@@ -741,6 +741,7 @@ export default {
741
  '15d': '12倩',
742
  '30d': '30倩',
743
  },
 
744
  },
745
  footer: {
746
  profile: 'β€œδΏη•™ζ‰€ζœ‰ζ¬Šεˆ© @ react”',
 
741
  '15d': '12倩',
742
  '30d': '30倩',
743
  },
744
+ publish: 'η™ΌεΈƒ',
745
  },
746
  footer: {
747
  profile: 'β€œδΏη•™ζ‰€ζœ‰ζ¬Šεˆ© @ react”',
web/src/locales/zh.ts CHANGED
@@ -759,6 +759,7 @@ export default {
759
  '15d': '12倩',
760
  '30d': '30倩',
761
  },
 
762
  },
763
  footer: {
764
  profile: 'All rights reserved @ React',
 
759
  '15d': '12倩',
760
  '30d': '30倩',
761
  },
762
+ publish: '发布',
763
  },
764
  footer: {
765
  profile: 'All rights reserved @ React',
web/src/pages/chat/hooks.ts CHANGED
@@ -1,20 +1,14 @@
1
  import { MessageType } from '@/constants/chat';
2
  import { fileIconMap } from '@/constants/common';
3
  import {
4
- useCreateToken,
5
  useFetchConversation,
6
  useFetchConversationList,
7
  useFetchDialog,
8
  useFetchDialogList,
9
- useFetchStats,
10
- useListToken,
11
  useRemoveConversation,
12
  useRemoveDialog,
13
- useRemoveToken,
14
  useSelectConversationList,
15
  useSelectDialogList,
16
- useSelectStats,
17
- useSelectTokenList,
18
  useSetDialog,
19
  useUpdateConversation,
20
  } from '@/hooks/chat-hooks';
@@ -25,16 +19,9 @@ import {
25
  } from '@/hooks/common-hooks';
26
  import { useSendMessageWithSse } from '@/hooks/logic-hooks';
27
  import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
28
- import {
29
- IAnswer,
30
- IConversation,
31
- IDialog,
32
- IStats,
33
- } from '@/interfaces/database/chat';
34
  import { IChunk } from '@/interfaces/database/knowledge';
35
  import { getFileExtension } from '@/utils';
36
- import { message } from 'antd';
37
- import dayjs, { Dayjs } from 'dayjs';
38
  import omit from 'lodash/omit';
39
  import trim from 'lodash/trim';
40
  import {
@@ -773,202 +760,3 @@ export const useSendButtonDisabled = (value: string) => {
773
  return trim(value) === '';
774
  };
775
  //#endregion
776
-
777
- //#region API provided for external calls
778
-
779
- type RangeValue = [Dayjs | null, Dayjs | null] | null;
780
-
781
- const getDay = (date: Dayjs) => date.format('YYYY-MM-DD');
782
-
783
- export const useFetchStatsOnMount = (visible: boolean) => {
784
- const fetchStats = useFetchStats();
785
- const [pickerValue, setPickerValue] = useState<RangeValue>([
786
- dayjs(),
787
- dayjs().subtract(7, 'day'),
788
- ]);
789
-
790
- useEffect(() => {
791
- if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
792
- fetchStats({
793
- fromDate: getDay(pickerValue[0]),
794
- toDate: getDay(pickerValue[1] ?? dayjs()),
795
- });
796
- }
797
- }, [fetchStats, pickerValue, visible]);
798
-
799
- return {
800
- pickerValue,
801
- setPickerValue,
802
- };
803
- };
804
-
805
- export const useOperateApiKey = (visible: boolean, dialogId: string) => {
806
- const removeToken = useRemoveToken();
807
- const createToken = useCreateToken(dialogId);
808
- const listToken = useListToken();
809
- const tokenList = useSelectTokenList();
810
- const creatingLoading = useOneNamespaceEffectsLoading('chatModel', [
811
- 'createToken',
812
- ]);
813
- const listLoading = useOneNamespaceEffectsLoading('chatModel', ['list']);
814
-
815
- const showDeleteConfirm = useShowDeleteConfirm();
816
-
817
- const onRemoveToken = (token: string, tenantId: string) => {
818
- showDeleteConfirm({
819
- onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
820
- });
821
- };
822
-
823
- useEffect(() => {
824
- if (visible && dialogId) {
825
- listToken(dialogId);
826
- }
827
- }, [listToken, dialogId, visible]);
828
-
829
- return {
830
- removeToken: onRemoveToken,
831
- createToken,
832
- tokenList,
833
- creatingLoading,
834
- listLoading,
835
- };
836
- };
837
-
838
- type ChartStatsType = {
839
- [k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
840
- };
841
-
842
- export const useSelectChartStatsList = (): ChartStatsType => {
843
- const stats: IStats = useSelectStats();
844
- // const stats = {
845
- // pv: [
846
- // ['2024-06-01', 1],
847
- // ['2024-07-24', 3],
848
- // ['2024-09-01', 10],
849
- // ],
850
- // uv: [
851
- // ['2024-02-01', 0],
852
- // ['2024-03-01', 99],
853
- // ['2024-05-01', 3],
854
- // ],
855
- // speed: [
856
- // ['2024-09-01', 2],
857
- // ['2024-09-01', 3],
858
- // ],
859
- // tokens: [
860
- // ['2024-09-01', 1],
861
- // ['2024-09-01', 3],
862
- // ],
863
- // round: [
864
- // ['2024-09-01', 0],
865
- // ['2024-09-01', 3],
866
- // ],
867
- // thumb_up: [
868
- // ['2024-09-01', 3],
869
- // ['2024-09-01', 9],
870
- // ],
871
- // };
872
-
873
- return Object.keys(stats).reduce((pre, cur) => {
874
- const item = stats[cur as keyof IStats];
875
- if (item.length > 0) {
876
- pre[cur as keyof IStats] = item.map((x) => ({
877
- xAxis: x[0] as string,
878
- yAxis: x[1] as number,
879
- }));
880
- }
881
- return pre;
882
- }, {} as ChartStatsType);
883
- };
884
-
885
- export const useShowTokenEmptyError = () => {
886
- const [messageApi, contextHolder] = message.useMessage();
887
- const { t } = useTranslate('chat');
888
-
889
- const showTokenEmptyError = useCallback(() => {
890
- messageApi.error(t('tokenError'));
891
- }, [messageApi, t]);
892
- return { showTokenEmptyError, contextHolder };
893
- };
894
-
895
- const getUrlWithToken = (token: string) => {
896
- const { protocol, host } = window.location;
897
- return `${protocol}//${host}/chat/share?shared_id=${token}`;
898
- };
899
-
900
- const useFetchTokenListBeforeOtherStep = (dialogId: string) => {
901
- const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
902
-
903
- const listToken = useListToken();
904
- const tokenList = useSelectTokenList();
905
-
906
- const token =
907
- Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
908
-
909
- const handleOperate = useCallback(async () => {
910
- const data = await listToken(dialogId);
911
- const list = data.data;
912
- if (data.retcode === 0 && Array.isArray(list) && list.length > 0) {
913
- return list[0]?.token;
914
- } else {
915
- showTokenEmptyError();
916
- return false;
917
- }
918
- }, [dialogId, listToken, showTokenEmptyError]);
919
-
920
- return {
921
- token,
922
- contextHolder,
923
- handleOperate,
924
- };
925
- };
926
-
927
- export const useShowEmbedModal = (dialogId: string) => {
928
- const {
929
- visible: embedVisible,
930
- hideModal: hideEmbedModal,
931
- showModal: showEmbedModal,
932
- } = useSetModalState();
933
-
934
- const { handleOperate, token, contextHolder } =
935
- useFetchTokenListBeforeOtherStep(dialogId);
936
-
937
- const handleShowEmbedModal = useCallback(async () => {
938
- const succeed = await handleOperate();
939
- if (succeed) {
940
- showEmbedModal();
941
- }
942
- }, [handleOperate, showEmbedModal]);
943
-
944
- return {
945
- showEmbedModal: handleShowEmbedModal,
946
- hideEmbedModal,
947
- embedVisible,
948
- embedToken: token,
949
- errorContextHolder: contextHolder,
950
- };
951
- };
952
-
953
- export const usePreviewChat = (dialogId: string) => {
954
- const { handleOperate, contextHolder } =
955
- useFetchTokenListBeforeOtherStep(dialogId);
956
-
957
- const open = useCallback((t: string) => {
958
- window.open(getUrlWithToken(t), '_blank');
959
- }, []);
960
-
961
- const handlePreview = useCallback(async () => {
962
- const token = await handleOperate();
963
- if (token) {
964
- open(token);
965
- }
966
- }, [handleOperate, open]);
967
-
968
- return {
969
- handlePreview,
970
- contextHolder,
971
- };
972
- };
973
-
974
- //#endregion
 
1
  import { MessageType } from '@/constants/chat';
2
  import { fileIconMap } from '@/constants/common';
3
  import {
 
4
  useFetchConversation,
5
  useFetchConversationList,
6
  useFetchDialog,
7
  useFetchDialogList,
 
 
8
  useRemoveConversation,
9
  useRemoveDialog,
 
10
  useSelectConversationList,
11
  useSelectDialogList,
 
 
12
  useSetDialog,
13
  useUpdateConversation,
14
  } from '@/hooks/chat-hooks';
 
19
  } from '@/hooks/common-hooks';
20
  import { useSendMessageWithSse } from '@/hooks/logic-hooks';
21
  import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
22
+ import { IAnswer, IConversation, IDialog } from '@/interfaces/database/chat';
 
 
 
 
 
23
  import { IChunk } from '@/interfaces/database/knowledge';
24
  import { getFileExtension } from '@/utils';
 
 
25
  import omit from 'lodash/omit';
26
  import trim from 'lodash/trim';
27
  import {
 
760
  return trim(value) === '';
761
  };
762
  //#endregion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
web/src/pages/chat/index.tsx CHANGED
@@ -41,10 +41,10 @@ import {
41
  useSelectFirstDialogOnMount,
42
  } from './hooks';
43
 
 
44
  import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
45
  import { useSetSelectedRecord } from '@/hooks/logic-hooks';
46
  import { IDialog } from '@/interfaces/database/chat';
47
- import ChatOverviewModal from './chat-overview-modal';
48
  import styles from './index.less';
49
 
50
  const { Text } = Typography;
@@ -371,11 +371,15 @@ const Chat = () => {
371
  initialName={initialConversationName}
372
  loading={conversationRenameLoading}
373
  ></RenameModal>
374
- <ChatOverviewModal
375
- visible={overviewVisible}
376
- hideModal={hideOverviewModal}
377
- dialog={currentRecord}
378
- ></ChatOverviewModal>
 
 
 
 
379
  </Flex>
380
  );
381
  };
 
41
  useSelectFirstDialogOnMount,
42
  } from './hooks';
43
 
44
+ import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
45
  import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
46
  import { useSetSelectedRecord } from '@/hooks/logic-hooks';
47
  import { IDialog } from '@/interfaces/database/chat';
 
48
  import styles from './index.less';
49
 
50
  const { Text } = Typography;
 
371
  initialName={initialConversationName}
372
  loading={conversationRenameLoading}
373
  ></RenameModal>
374
+ {overviewVisible && (
375
+ <ChatOverviewModal
376
+ visible={overviewVisible}
377
+ hideModal={hideOverviewModal}
378
+ id={currentRecord.id}
379
+ name={currentRecord.name}
380
+ idKey="dialogId"
381
+ ></ChatOverviewModal>
382
+ )}
383
  </Flex>
384
  );
385
  };
web/src/pages/chat/model.ts CHANGED
@@ -1,14 +1,7 @@
1
- import {
2
- IConversation,
3
- IDialog,
4
- IStats,
5
- IToken,
6
- Message,
7
- } from '@/interfaces/database/chat';
8
  import i18n from '@/locales/config';
9
  import chatService from '@/services/chat-service';
10
  import { message } from 'antd';
11
- import omit from 'lodash/omit';
12
  import { DvaModel } from 'umi';
13
  import { v4 as uuid } from 'uuid';
14
  import { IClientConversation, IMessage } from './interface';
@@ -20,8 +13,6 @@ export interface ChatModelState {
20
  currentDialog: IDialog;
21
  conversationList: IConversation[];
22
  currentConversation: IClientConversation;
23
- tokenList: IToken[];
24
- stats: IStats;
25
  }
26
 
27
  const model: DvaModel<ChatModelState> = {
@@ -32,8 +23,6 @@ const model: DvaModel<ChatModelState> = {
32
  currentDialog: <IDialog>{},
33
  conversationList: [],
34
  currentConversation: {} as IClientConversation,
35
- tokenList: [],
36
- stats: {} as IStats,
37
  },
38
  reducers: {
39
  save(state, action) {
@@ -71,18 +60,6 @@ const model: DvaModel<ChatModelState> = {
71
  currentConversation: { ...payload, message: messageList },
72
  };
73
  },
74
- setTokenList(state, { payload }) {
75
- return {
76
- ...state,
77
- tokenList: payload,
78
- };
79
- },
80
- setStats(state, { payload }) {
81
- return {
82
- ...state,
83
- stats: payload,
84
- };
85
- },
86
  },
87
 
88
  effects: {
@@ -183,51 +160,6 @@ const model: DvaModel<ChatModelState> = {
183
  }
184
  return data.retcode;
185
  },
186
- *createToken({ payload }, { call, put }) {
187
- const { data } = yield call(chatService.createToken, payload);
188
- if (data.retcode === 0) {
189
- yield put({
190
- type: 'listToken',
191
- payload: payload,
192
- });
193
- message.success(i18n.t('message.created'));
194
- }
195
- return data;
196
- },
197
- *listToken({ payload }, { call, put }) {
198
- const { data } = yield call(chatService.listToken, payload);
199
- if (data.retcode === 0) {
200
- yield put({
201
- type: 'setTokenList',
202
- payload: data.data,
203
- });
204
- }
205
- return data;
206
- },
207
- *removeToken({ payload }, { call, put }) {
208
- const { data } = yield call(
209
- chatService.removeToken,
210
- omit(payload, ['dialogId']),
211
- );
212
- if (data.retcode === 0) {
213
- message.success(i18n.t('message.deleted'));
214
- yield put({
215
- type: 'listToken',
216
- payload: { dialog_id: payload.dialogId },
217
- });
218
- }
219
- return data.retcode;
220
- },
221
- *getStats({ payload }, { call, put }) {
222
- const { data } = yield call(chatService.getStats, payload);
223
- if (data.retcode === 0) {
224
- yield put({
225
- type: 'setStats',
226
- payload: data.data,
227
- });
228
- }
229
- return data.retcode;
230
- },
231
  *createExternalConversation({ payload }, { call, put }) {
232
  const { data } = yield call(
233
  chatService.createExternalConversation,
 
1
+ import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
 
 
 
 
 
 
2
  import i18n from '@/locales/config';
3
  import chatService from '@/services/chat-service';
4
  import { message } from 'antd';
 
5
  import { DvaModel } from 'umi';
6
  import { v4 as uuid } from 'uuid';
7
  import { IClientConversation, IMessage } from './interface';
 
13
  currentDialog: IDialog;
14
  conversationList: IConversation[];
15
  currentConversation: IClientConversation;
 
 
16
  }
17
 
18
  const model: DvaModel<ChatModelState> = {
 
23
  currentDialog: <IDialog>{},
24
  conversationList: [],
25
  currentConversation: {} as IClientConversation,
 
 
26
  },
27
  reducers: {
28
  save(state, action) {
 
60
  currentConversation: { ...payload, message: messageList },
61
  };
62
  },
 
 
 
 
 
 
 
 
 
 
 
 
63
  },
64
 
65
  effects: {
 
160
  }
161
  return data.retcode;
162
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  *createExternalConversation({ payload }, { call, put }) {
164
  const { data } = yield call(
165
  chatService.createExternalConversation,
web/src/pages/flow/header/index.tsx CHANGED
@@ -1,8 +1,9 @@
1
- import { useTranslate } from '@/hooks/common-hooks';
 
2
  import { useFetchFlow } from '@/hooks/flow-hooks';
3
  import { ArrowLeftOutlined } from '@ant-design/icons';
4
  import { Button, Flex, Space } from 'antd';
5
- import { Link } from 'umi';
6
  import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
7
  import styles from './index.less';
8
 
@@ -15,6 +16,12 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
15
  const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
16
  const { data } = useFetchFlow();
17
  const { t } = useTranslate('flow');
 
 
 
 
 
 
18
 
19
  return (
20
  <>
@@ -37,8 +44,19 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
37
  <Button type="primary" onClick={saveGraph}>
38
  <b>{t('save')}</b>
39
  </Button>
 
 
 
40
  </Space>
41
  </Flex>
 
 
 
 
 
 
 
 
42
  </>
43
  );
44
  };
 
1
+ import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
2
+ import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
3
  import { useFetchFlow } from '@/hooks/flow-hooks';
4
  import { ArrowLeftOutlined } from '@ant-design/icons';
5
  import { Button, Flex, Space } from 'antd';
6
+ import { Link, useParams } from 'umi';
7
  import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
8
  import styles from './index.less';
9
 
 
16
  const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
17
  const { data } = useFetchFlow();
18
  const { t } = useTranslate('flow');
19
+ const {
20
+ visible: overviewVisible,
21
+ hideModal: hideOverviewModal,
22
+ showModal: showOverviewModal,
23
+ } = useSetModalState();
24
+ const { id } = useParams();
25
 
26
  return (
27
  <>
 
44
  <Button type="primary" onClick={saveGraph}>
45
  <b>{t('save')}</b>
46
  </Button>
47
+ <Button type="primary" onClick={showOverviewModal}>
48
+ <b>{t('publish')}</b>
49
+ </Button>
50
  </Space>
51
  </Flex>
52
+ {overviewVisible && (
53
+ <ChatOverviewModal
54
+ visible={overviewVisible}
55
+ hideModal={hideOverviewModal}
56
+ id={id!}
57
+ idKey="canvasId"
58
+ ></ChatOverviewModal>
59
+ )}
60
  </>
61
  );
62
  };