mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-05-29 17:45:33 +08:00
### 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)
This commit is contained in:
parent
37be0ff3d3
commit
827042f72b
@ -9,12 +9,12 @@ import { Button, Modal, Space, Table } from 'antd';
|
|||||||
import { useOperateApiKey } from '../hooks';
|
import { useOperateApiKey } from '../hooks';
|
||||||
|
|
||||||
const ChatApiKeyModal = ({
|
const ChatApiKeyModal = ({
|
||||||
visible,
|
|
||||||
dialogId,
|
dialogId,
|
||||||
hideModal,
|
hideModal,
|
||||||
}: IModalProps<any> & { dialogId: string }) => {
|
idKey,
|
||||||
|
}: IModalProps<any> & { dialogId: string; idKey: string }) => {
|
||||||
const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
|
const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
|
||||||
useOperateApiKey(visible, dialogId);
|
useOperateApiKey(dialogId, idKey);
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
const columns: TableProps<IToken>['columns'] = [
|
const columns: TableProps<IToken>['columns'] = [
|
||||||
@ -48,7 +48,7 @@ const ChatApiKeyModal = ({
|
|||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
title={t('apiKey')}
|
title={t('apiKey')}
|
||||||
open={visible}
|
open
|
||||||
onCancel={hideModal}
|
onCancel={hideModal}
|
||||||
cancelButtonProps={{ style: { display: 'none' } }}
|
cancelButtonProps={{ style: { display: 'none' } }}
|
||||||
style={{ top: 300 }}
|
style={{ top: 300 }}
|
@ -1,7 +1,8 @@
|
|||||||
import LineChart from '@/components/line-chart';
|
import LineChart from '@/components/line-chart';
|
||||||
|
import { useFetchNextStats } from '@/hooks/chat-hooks';
|
||||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { IDialog, IStats } from '@/interfaces/database/chat';
|
import { IStats } from '@/interfaces/database/chat';
|
||||||
import { formatDate } from '@/utils/date';
|
import { formatDate } from '@/utils/date';
|
||||||
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
||||||
import { RangePickerProps } from 'antd/es/date-picker';
|
import { RangePickerProps } from 'antd/es/date-picker';
|
||||||
@ -10,7 +11,6 @@ import camelCase from 'lodash/camelCase';
|
|||||||
import ChatApiKeyModal from '../chat-api-key-modal';
|
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||||
import EmbedModal from '../embed-modal';
|
import EmbedModal from '../embed-modal';
|
||||||
import {
|
import {
|
||||||
useFetchStatsOnMount,
|
|
||||||
usePreviewChat,
|
usePreviewChat,
|
||||||
useSelectChartStatsList,
|
useSelectChartStatsList,
|
||||||
useShowEmbedModal,
|
useShowEmbedModal,
|
||||||
@ -40,8 +40,10 @@ const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
|
|||||||
const ChatOverviewModal = ({
|
const ChatOverviewModal = ({
|
||||||
visible,
|
visible,
|
||||||
hideModal,
|
hideModal,
|
||||||
dialog,
|
id,
|
||||||
}: IModalProps<any> & { dialog: IDialog }) => {
|
name = '',
|
||||||
|
idKey,
|
||||||
|
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
const {
|
const {
|
||||||
visible: apiKeyVisible,
|
visible: apiKeyVisible,
|
||||||
@ -54,15 +56,15 @@ const ChatOverviewModal = ({
|
|||||||
showEmbedModal,
|
showEmbedModal,
|
||||||
embedToken,
|
embedToken,
|
||||||
errorContextHolder,
|
errorContextHolder,
|
||||||
} = useShowEmbedModal(dialog.id);
|
} = useShowEmbedModal(id, idKey);
|
||||||
|
|
||||||
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
const { pickerValue, setPickerValue } = useFetchNextStats();
|
||||||
|
|
||||||
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
||||||
return current && current > dayjs().endOf('day');
|
return current && current > dayjs().endOf('day');
|
||||||
};
|
};
|
||||||
|
|
||||||
const { handlePreview, contextHolder } = usePreviewChat(dialog.id);
|
const { handlePreview, contextHolder } = usePreviewChat(id, idKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -97,7 +99,7 @@ const ChatOverviewModal = ({
|
|||||||
</a>
|
</a>
|
||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title={`${dialog.name} Web App`}>
|
<Card title={`${name} Web App`}>
|
||||||
<Flex gap={8} vertical>
|
<Flex gap={8} vertical>
|
||||||
<Space size={'middle'}>
|
<Space size={'middle'}>
|
||||||
<Button onClick={handlePreview}>{t('preview')}</Button>
|
<Button onClick={handlePreview}>{t('preview')}</Button>
|
||||||
@ -124,11 +126,13 @@ const ChatOverviewModal = ({
|
|||||||
<StatsLineChart statsType={'uv'}></StatsLineChart>
|
<StatsLineChart statsType={'uv'}></StatsLineChart>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
<ChatApiKeyModal
|
{apiKeyVisible && (
|
||||||
visible={apiKeyVisible}
|
<ChatApiKeyModal
|
||||||
hideModal={hideApiKeyModal}
|
hideModal={hideApiKeyModal}
|
||||||
dialogId={dialog.id}
|
dialogId={id}
|
||||||
></ChatApiKeyModal>
|
idKey={idKey}
|
||||||
|
></ChatApiKeyModal>
|
||||||
|
)}
|
||||||
<EmbedModal
|
<EmbedModal
|
||||||
token={embedToken}
|
token={embedToken}
|
||||||
visible={embedVisible}
|
visible={embedVisible}
|
151
web/src/components/api-service/hooks.ts
Normal file
151
web/src/components/api-service/hooks.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import {
|
||||||
|
useCreateNextToken,
|
||||||
|
useFetchNextStats,
|
||||||
|
useFetchTokenList,
|
||||||
|
useRemoveNextToken,
|
||||||
|
} from '@/hooks/chat-hooks';
|
||||||
|
import {
|
||||||
|
useSetModalState,
|
||||||
|
useShowDeleteConfirm,
|
||||||
|
useTranslate,
|
||||||
|
} from '@/hooks/common-hooks';
|
||||||
|
import { IStats } from '@/interfaces/database/chat';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
export const useOperateApiKey = (dialogId: string, idKey: string) => {
|
||||||
|
const { removeToken } = useRemoveNextToken();
|
||||||
|
const { createToken, loading: creatingLoading } = useCreateNextToken();
|
||||||
|
const { data: tokenList, loading: listLoading } = useFetchTokenList({
|
||||||
|
[idKey]: dialogId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const showDeleteConfirm = useShowDeleteConfirm();
|
||||||
|
|
||||||
|
const onRemoveToken = (token: string, tenantId: string) => {
|
||||||
|
showDeleteConfirm({
|
||||||
|
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCreateToken = useCallback(() => {
|
||||||
|
createToken({ [idKey]: dialogId });
|
||||||
|
}, [createToken, idKey, dialogId]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
removeToken: onRemoveToken,
|
||||||
|
createToken: onCreateToken,
|
||||||
|
tokenList,
|
||||||
|
creatingLoading,
|
||||||
|
listLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChartStatsType = {
|
||||||
|
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectChartStatsList = (): ChartStatsType => {
|
||||||
|
const { data: stats } = useFetchNextStats();
|
||||||
|
|
||||||
|
return Object.keys(stats).reduce((pre, cur) => {
|
||||||
|
const item = stats[cur as keyof IStats];
|
||||||
|
if (item.length > 0) {
|
||||||
|
pre[cur as keyof IStats] = item.map((x) => ({
|
||||||
|
xAxis: x[0] as string,
|
||||||
|
yAxis: x[1] as number,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}, {} as ChartStatsType);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useShowTokenEmptyError = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
|
const showTokenEmptyError = useCallback(() => {
|
||||||
|
messageApi.error(t('tokenError'));
|
||||||
|
}, [messageApi, t]);
|
||||||
|
return { showTokenEmptyError, contextHolder };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUrlWithToken = (token: string) => {
|
||||||
|
const { protocol, host } = window.location;
|
||||||
|
return `${protocol}//${host}/chat/share?shared_id=${token}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
|
||||||
|
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
|
||||||
|
|
||||||
|
const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });
|
||||||
|
|
||||||
|
const token =
|
||||||
|
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
|
||||||
|
|
||||||
|
const handleOperate = useCallback(async () => {
|
||||||
|
const ret = await refetch();
|
||||||
|
const list = ret.data;
|
||||||
|
if (Array.isArray(list) && list.length > 0) {
|
||||||
|
return list[0]?.token;
|
||||||
|
} else {
|
||||||
|
showTokenEmptyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [showTokenEmptyError, refetch]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
contextHolder,
|
||||||
|
handleOperate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useShowEmbedModal = (dialogId: string, idKey: string) => {
|
||||||
|
const {
|
||||||
|
visible: embedVisible,
|
||||||
|
hideModal: hideEmbedModal,
|
||||||
|
showModal: showEmbedModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
|
||||||
|
const { handleOperate, token, contextHolder } =
|
||||||
|
useFetchTokenListBeforeOtherStep(dialogId, idKey);
|
||||||
|
|
||||||
|
const handleShowEmbedModal = useCallback(async () => {
|
||||||
|
const succeed = await handleOperate();
|
||||||
|
if (succeed) {
|
||||||
|
showEmbedModal();
|
||||||
|
}
|
||||||
|
}, [handleOperate, showEmbedModal]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
showEmbedModal: handleShowEmbedModal,
|
||||||
|
hideEmbedModal,
|
||||||
|
embedVisible,
|
||||||
|
embedToken: token,
|
||||||
|
errorContextHolder: contextHolder,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePreviewChat = (dialogId: string, idKey: string) => {
|
||||||
|
const { handleOperate, contextHolder } = useFetchTokenListBeforeOtherStep(
|
||||||
|
dialogId,
|
||||||
|
idKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
const open = useCallback((t: string) => {
|
||||||
|
window.open(getUrlWithToken(t), '_blank');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePreview = useCallback(async () => {
|
||||||
|
const token = await handleOperate();
|
||||||
|
if (token) {
|
||||||
|
open(token);
|
||||||
|
}
|
||||||
|
}, [handleOperate, open]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handlePreview,
|
||||||
|
contextHolder,
|
||||||
|
};
|
||||||
|
};
|
@ -4,7 +4,10 @@ import {
|
|||||||
IStats,
|
IStats,
|
||||||
IToken,
|
IToken,
|
||||||
} from '@/interfaces/database/chat';
|
} from '@/interfaces/database/chat';
|
||||||
import { useCallback } from 'react';
|
import chatService from '@/services/chat-service';
|
||||||
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
export const useFetchDialogList = () => {
|
export const useFetchDialogList = () => {
|
||||||
@ -175,79 +178,108 @@ export const useCompleteConversation = () => {
|
|||||||
|
|
||||||
// #region API provided for external calls
|
// #region API provided for external calls
|
||||||
|
|
||||||
export const useCreateToken = (dialogId: string) => {
|
export const useCreateToken = (params: Record<string, any>) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const createToken = useCallback(() => {
|
const createToken = useCallback(() => {
|
||||||
return dispatch<any>({
|
return dispatch<any>({
|
||||||
type: 'chatModel/createToken',
|
type: 'chatModel/createToken',
|
||||||
payload: { dialogId },
|
payload: params,
|
||||||
});
|
});
|
||||||
}, [dispatch, dialogId]);
|
}, [dispatch, params]);
|
||||||
|
|
||||||
return createToken;
|
return createToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useListToken = () => {
|
export const useCreateNextToken = () => {
|
||||||
const dispatch = useDispatch();
|
const queryClient = useQueryClient();
|
||||||
|
const {
|
||||||
const listToken = useCallback(
|
data,
|
||||||
(dialogId: string) => {
|
isPending: loading,
|
||||||
return dispatch<any>({
|
mutateAsync,
|
||||||
type: 'chatModel/listToken',
|
} = useMutation({
|
||||||
payload: { dialogId },
|
mutationKey: ['createToken'],
|
||||||
});
|
mutationFn: async (params: Record<string, any>) => {
|
||||||
|
const { data } = await chatService.createToken(params);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
|
||||||
|
}
|
||||||
|
return data?.data ?? [];
|
||||||
},
|
},
|
||||||
[dispatch],
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return listToken;
|
return { data, loading, createToken: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectTokenList = () => {
|
export const useFetchTokenList = (params: Record<string, any>) => {
|
||||||
const tokenList: IToken[] = useSelector(
|
const {
|
||||||
(state: any) => state.chatModel.tokenList,
|
data,
|
||||||
);
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<IToken[]>({
|
||||||
|
queryKey: ['fetchTokenList', params],
|
||||||
|
initialData: [],
|
||||||
|
gcTime: 0,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await chatService.listToken(params);
|
||||||
|
|
||||||
return tokenList;
|
return data?.data ?? [];
|
||||||
};
|
|
||||||
|
|
||||||
export const useRemoveToken = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const removeToken = useCallback(
|
|
||||||
(payload: { tenantId: string; dialogId: string; tokens: string[] }) => {
|
|
||||||
return dispatch<any>({
|
|
||||||
type: 'chatModel/removeToken',
|
|
||||||
payload: payload,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[dispatch],
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return removeToken;
|
return { data, loading, refetch };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFetchStats = () => {
|
export const useRemoveNextToken = () => {
|
||||||
const dispatch = useDispatch();
|
const queryClient = useQueryClient();
|
||||||
|
const {
|
||||||
const fetchStats = useCallback(
|
data,
|
||||||
(payload: any) => {
|
isPending: loading,
|
||||||
return dispatch<any>({
|
mutateAsync,
|
||||||
type: 'chatModel/getStats',
|
} = useMutation({
|
||||||
payload,
|
mutationKey: ['removeToken'],
|
||||||
});
|
mutationFn: async (params: {
|
||||||
|
tenantId: string;
|
||||||
|
dialogId: string;
|
||||||
|
tokens: string[];
|
||||||
|
}) => {
|
||||||
|
const { data } = await chatService.removeToken(params);
|
||||||
|
if (data.retcode === 0) {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
|
||||||
|
}
|
||||||
|
return data?.data ?? [];
|
||||||
},
|
},
|
||||||
[dispatch],
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return fetchStats;
|
return { data, loading, removeToken: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectStats = () => {
|
type RangeValue = [Dayjs | null, Dayjs | null] | null;
|
||||||
const stats: IStats = useSelector((state: any) => state.chatModel.stats);
|
|
||||||
|
|
||||||
return stats;
|
const getDay = (date?: Dayjs) => date?.format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
export const useFetchNextStats = () => {
|
||||||
|
const [pickerValue, setPickerValue] = useState<RangeValue>([
|
||||||
|
dayjs(),
|
||||||
|
dayjs().subtract(7, 'day'),
|
||||||
|
]);
|
||||||
|
const { data, isFetching: loading } = useQuery<IStats>({
|
||||||
|
queryKey: ['fetchStats', pickerValue],
|
||||||
|
initialData: {} as IStats,
|
||||||
|
gcTime: 0,
|
||||||
|
queryFn: async () => {
|
||||||
|
if (Array.isArray(pickerValue) && pickerValue[0]) {
|
||||||
|
const { data } = await chatService.getStats({
|
||||||
|
fromDate: getDay(pickerValue[0]),
|
||||||
|
toDate: getDay(pickerValue[1] ?? dayjs()),
|
||||||
|
});
|
||||||
|
return data?.data ?? {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, pickerValue, setPickerValue };
|
||||||
};
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -783,6 +783,7 @@ The above is the content you need to summarize.`,
|
|||||||
'15d': '12 days',
|
'15d': '12 days',
|
||||||
'30d': '30 days',
|
'30d': '30 days',
|
||||||
},
|
},
|
||||||
|
publish: 'Publish',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -741,6 +741,7 @@ export default {
|
|||||||
'15d': '12天',
|
'15d': '12天',
|
||||||
'30d': '30天',
|
'30d': '30天',
|
||||||
},
|
},
|
||||||
|
publish: '發布',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: '“保留所有權利 @ react”',
|
profile: '“保留所有權利 @ react”',
|
||||||
|
@ -759,6 +759,7 @@ export default {
|
|||||||
'15d': '12天',
|
'15d': '12天',
|
||||||
'30d': '30天',
|
'30d': '30天',
|
||||||
},
|
},
|
||||||
|
publish: '发布',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
import { MessageType } from '@/constants/chat';
|
import { MessageType } from '@/constants/chat';
|
||||||
import { fileIconMap } from '@/constants/common';
|
import { fileIconMap } from '@/constants/common';
|
||||||
import {
|
import {
|
||||||
useCreateToken,
|
|
||||||
useFetchConversation,
|
useFetchConversation,
|
||||||
useFetchConversationList,
|
useFetchConversationList,
|
||||||
useFetchDialog,
|
useFetchDialog,
|
||||||
useFetchDialogList,
|
useFetchDialogList,
|
||||||
useFetchStats,
|
|
||||||
useListToken,
|
|
||||||
useRemoveConversation,
|
useRemoveConversation,
|
||||||
useRemoveDialog,
|
useRemoveDialog,
|
||||||
useRemoveToken,
|
|
||||||
useSelectConversationList,
|
useSelectConversationList,
|
||||||
useSelectDialogList,
|
useSelectDialogList,
|
||||||
useSelectStats,
|
|
||||||
useSelectTokenList,
|
|
||||||
useSetDialog,
|
useSetDialog,
|
||||||
useUpdateConversation,
|
useUpdateConversation,
|
||||||
} from '@/hooks/chat-hooks';
|
} from '@/hooks/chat-hooks';
|
||||||
@ -25,16 +19,9 @@ import {
|
|||||||
} from '@/hooks/common-hooks';
|
} from '@/hooks/common-hooks';
|
||||||
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
|
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
|
||||||
import {
|
import { IAnswer, IConversation, IDialog } from '@/interfaces/database/chat';
|
||||||
IAnswer,
|
|
||||||
IConversation,
|
|
||||||
IDialog,
|
|
||||||
IStats,
|
|
||||||
} from '@/interfaces/database/chat';
|
|
||||||
import { IChunk } from '@/interfaces/database/knowledge';
|
import { IChunk } from '@/interfaces/database/knowledge';
|
||||||
import { getFileExtension } from '@/utils';
|
import { getFileExtension } from '@/utils';
|
||||||
import { message } from 'antd';
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import trim from 'lodash/trim';
|
import trim from 'lodash/trim';
|
||||||
import {
|
import {
|
||||||
@ -773,202 +760,3 @@ export const useSendButtonDisabled = (value: string) => {
|
|||||||
return trim(value) === '';
|
return trim(value) === '';
|
||||||
};
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region API provided for external calls
|
|
||||||
|
|
||||||
type RangeValue = [Dayjs | null, Dayjs | null] | null;
|
|
||||||
|
|
||||||
const getDay = (date: Dayjs) => date.format('YYYY-MM-DD');
|
|
||||||
|
|
||||||
export const useFetchStatsOnMount = (visible: boolean) => {
|
|
||||||
const fetchStats = useFetchStats();
|
|
||||||
const [pickerValue, setPickerValue] = useState<RangeValue>([
|
|
||||||
dayjs(),
|
|
||||||
dayjs().subtract(7, 'day'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
|
|
||||||
fetchStats({
|
|
||||||
fromDate: getDay(pickerValue[0]),
|
|
||||||
toDate: getDay(pickerValue[1] ?? dayjs()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [fetchStats, pickerValue, visible]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
pickerValue,
|
|
||||||
setPickerValue,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useOperateApiKey = (visible: boolean, dialogId: string) => {
|
|
||||||
const removeToken = useRemoveToken();
|
|
||||||
const createToken = useCreateToken(dialogId);
|
|
||||||
const listToken = useListToken();
|
|
||||||
const tokenList = useSelectTokenList();
|
|
||||||
const creatingLoading = useOneNamespaceEffectsLoading('chatModel', [
|
|
||||||
'createToken',
|
|
||||||
]);
|
|
||||||
const listLoading = useOneNamespaceEffectsLoading('chatModel', ['list']);
|
|
||||||
|
|
||||||
const showDeleteConfirm = useShowDeleteConfirm();
|
|
||||||
|
|
||||||
const onRemoveToken = (token: string, tenantId: string) => {
|
|
||||||
showDeleteConfirm({
|
|
||||||
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible && dialogId) {
|
|
||||||
listToken(dialogId);
|
|
||||||
}
|
|
||||||
}, [listToken, dialogId, visible]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
removeToken: onRemoveToken,
|
|
||||||
createToken,
|
|
||||||
tokenList,
|
|
||||||
creatingLoading,
|
|
||||||
listLoading,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ChartStatsType = {
|
|
||||||
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSelectChartStatsList = (): ChartStatsType => {
|
|
||||||
const stats: IStats = useSelectStats();
|
|
||||||
// const stats = {
|
|
||||||
// pv: [
|
|
||||||
// ['2024-06-01', 1],
|
|
||||||
// ['2024-07-24', 3],
|
|
||||||
// ['2024-09-01', 10],
|
|
||||||
// ],
|
|
||||||
// uv: [
|
|
||||||
// ['2024-02-01', 0],
|
|
||||||
// ['2024-03-01', 99],
|
|
||||||
// ['2024-05-01', 3],
|
|
||||||
// ],
|
|
||||||
// speed: [
|
|
||||||
// ['2024-09-01', 2],
|
|
||||||
// ['2024-09-01', 3],
|
|
||||||
// ],
|
|
||||||
// tokens: [
|
|
||||||
// ['2024-09-01', 1],
|
|
||||||
// ['2024-09-01', 3],
|
|
||||||
// ],
|
|
||||||
// round: [
|
|
||||||
// ['2024-09-01', 0],
|
|
||||||
// ['2024-09-01', 3],
|
|
||||||
// ],
|
|
||||||
// thumb_up: [
|
|
||||||
// ['2024-09-01', 3],
|
|
||||||
// ['2024-09-01', 9],
|
|
||||||
// ],
|
|
||||||
// };
|
|
||||||
|
|
||||||
return Object.keys(stats).reduce((pre, cur) => {
|
|
||||||
const item = stats[cur as keyof IStats];
|
|
||||||
if (item.length > 0) {
|
|
||||||
pre[cur as keyof IStats] = item.map((x) => ({
|
|
||||||
xAxis: x[0] as string,
|
|
||||||
yAxis: x[1] as number,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return pre;
|
|
||||||
}, {} as ChartStatsType);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useShowTokenEmptyError = () => {
|
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
|
||||||
const { t } = useTranslate('chat');
|
|
||||||
|
|
||||||
const showTokenEmptyError = useCallback(() => {
|
|
||||||
messageApi.error(t('tokenError'));
|
|
||||||
}, [messageApi, t]);
|
|
||||||
return { showTokenEmptyError, contextHolder };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUrlWithToken = (token: string) => {
|
|
||||||
const { protocol, host } = window.location;
|
|
||||||
return `${protocol}//${host}/chat/share?shared_id=${token}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const useFetchTokenListBeforeOtherStep = (dialogId: string) => {
|
|
||||||
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
|
|
||||||
|
|
||||||
const listToken = useListToken();
|
|
||||||
const tokenList = useSelectTokenList();
|
|
||||||
|
|
||||||
const token =
|
|
||||||
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
|
|
||||||
|
|
||||||
const handleOperate = useCallback(async () => {
|
|
||||||
const data = await listToken(dialogId);
|
|
||||||
const list = data.data;
|
|
||||||
if (data.retcode === 0 && Array.isArray(list) && list.length > 0) {
|
|
||||||
return list[0]?.token;
|
|
||||||
} else {
|
|
||||||
showTokenEmptyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, [dialogId, listToken, showTokenEmptyError]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
token,
|
|
||||||
contextHolder,
|
|
||||||
handleOperate,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useShowEmbedModal = (dialogId: string) => {
|
|
||||||
const {
|
|
||||||
visible: embedVisible,
|
|
||||||
hideModal: hideEmbedModal,
|
|
||||||
showModal: showEmbedModal,
|
|
||||||
} = useSetModalState();
|
|
||||||
|
|
||||||
const { handleOperate, token, contextHolder } =
|
|
||||||
useFetchTokenListBeforeOtherStep(dialogId);
|
|
||||||
|
|
||||||
const handleShowEmbedModal = useCallback(async () => {
|
|
||||||
const succeed = await handleOperate();
|
|
||||||
if (succeed) {
|
|
||||||
showEmbedModal();
|
|
||||||
}
|
|
||||||
}, [handleOperate, showEmbedModal]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
showEmbedModal: handleShowEmbedModal,
|
|
||||||
hideEmbedModal,
|
|
||||||
embedVisible,
|
|
||||||
embedToken: token,
|
|
||||||
errorContextHolder: contextHolder,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePreviewChat = (dialogId: string) => {
|
|
||||||
const { handleOperate, contextHolder } =
|
|
||||||
useFetchTokenListBeforeOtherStep(dialogId);
|
|
||||||
|
|
||||||
const open = useCallback((t: string) => {
|
|
||||||
window.open(getUrlWithToken(t), '_blank');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handlePreview = useCallback(async () => {
|
|
||||||
const token = await handleOperate();
|
|
||||||
if (token) {
|
|
||||||
open(token);
|
|
||||||
}
|
|
||||||
}, [handleOperate, open]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
handlePreview,
|
|
||||||
contextHolder,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
@ -41,10 +41,10 @@ import {
|
|||||||
useSelectFirstDialogOnMount,
|
useSelectFirstDialogOnMount,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
|
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
|
||||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||||
import { IDialog } from '@/interfaces/database/chat';
|
import { IDialog } from '@/interfaces/database/chat';
|
||||||
import ChatOverviewModal from './chat-overview-modal';
|
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
@ -371,11 +371,15 @@ const Chat = () => {
|
|||||||
initialName={initialConversationName}
|
initialName={initialConversationName}
|
||||||
loading={conversationRenameLoading}
|
loading={conversationRenameLoading}
|
||||||
></RenameModal>
|
></RenameModal>
|
||||||
<ChatOverviewModal
|
{overviewVisible && (
|
||||||
visible={overviewVisible}
|
<ChatOverviewModal
|
||||||
hideModal={hideOverviewModal}
|
visible={overviewVisible}
|
||||||
dialog={currentRecord}
|
hideModal={hideOverviewModal}
|
||||||
></ChatOverviewModal>
|
id={currentRecord.id}
|
||||||
|
name={currentRecord.name}
|
||||||
|
idKey="dialogId"
|
||||||
|
></ChatOverviewModal>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
import {
|
import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
|
||||||
IConversation,
|
|
||||||
IDialog,
|
|
||||||
IStats,
|
|
||||||
IToken,
|
|
||||||
Message,
|
|
||||||
} from '@/interfaces/database/chat';
|
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import chatService from '@/services/chat-service';
|
import chatService from '@/services/chat-service';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import omit from 'lodash/omit';
|
|
||||||
import { DvaModel } from 'umi';
|
import { DvaModel } from 'umi';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { IClientConversation, IMessage } from './interface';
|
import { IClientConversation, IMessage } from './interface';
|
||||||
@ -20,8 +13,6 @@ export interface ChatModelState {
|
|||||||
currentDialog: IDialog;
|
currentDialog: IDialog;
|
||||||
conversationList: IConversation[];
|
conversationList: IConversation[];
|
||||||
currentConversation: IClientConversation;
|
currentConversation: IClientConversation;
|
||||||
tokenList: IToken[];
|
|
||||||
stats: IStats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const model: DvaModel<ChatModelState> = {
|
const model: DvaModel<ChatModelState> = {
|
||||||
@ -32,8 +23,6 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
currentDialog: <IDialog>{},
|
currentDialog: <IDialog>{},
|
||||||
conversationList: [],
|
conversationList: [],
|
||||||
currentConversation: {} as IClientConversation,
|
currentConversation: {} as IClientConversation,
|
||||||
tokenList: [],
|
|
||||||
stats: {} as IStats,
|
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
save(state, action) {
|
save(state, action) {
|
||||||
@ -71,18 +60,6 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
currentConversation: { ...payload, message: messageList },
|
currentConversation: { ...payload, message: messageList },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setTokenList(state, { payload }) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
tokenList: payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setStats(state, { payload }) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
stats: payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
@ -183,51 +160,6 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
}
|
}
|
||||||
return data.retcode;
|
return data.retcode;
|
||||||
},
|
},
|
||||||
*createToken({ payload }, { call, put }) {
|
|
||||||
const { data } = yield call(chatService.createToken, payload);
|
|
||||||
if (data.retcode === 0) {
|
|
||||||
yield put({
|
|
||||||
type: 'listToken',
|
|
||||||
payload: payload,
|
|
||||||
});
|
|
||||||
message.success(i18n.t('message.created'));
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
*listToken({ payload }, { call, put }) {
|
|
||||||
const { data } = yield call(chatService.listToken, payload);
|
|
||||||
if (data.retcode === 0) {
|
|
||||||
yield put({
|
|
||||||
type: 'setTokenList',
|
|
||||||
payload: data.data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
*removeToken({ payload }, { call, put }) {
|
|
||||||
const { data } = yield call(
|
|
||||||
chatService.removeToken,
|
|
||||||
omit(payload, ['dialogId']),
|
|
||||||
);
|
|
||||||
if (data.retcode === 0) {
|
|
||||||
message.success(i18n.t('message.deleted'));
|
|
||||||
yield put({
|
|
||||||
type: 'listToken',
|
|
||||||
payload: { dialog_id: payload.dialogId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return data.retcode;
|
|
||||||
},
|
|
||||||
*getStats({ payload }, { call, put }) {
|
|
||||||
const { data } = yield call(chatService.getStats, payload);
|
|
||||||
if (data.retcode === 0) {
|
|
||||||
yield put({
|
|
||||||
type: 'setStats',
|
|
||||||
payload: data.data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return data.retcode;
|
|
||||||
},
|
|
||||||
*createExternalConversation({ payload }, { call, put }) {
|
*createExternalConversation({ payload }, { call, put }) {
|
||||||
const { data } = yield call(
|
const { data } = yield call(
|
||||||
chatService.createExternalConversation,
|
chatService.createExternalConversation,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
|
||||||
|
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useFetchFlow } from '@/hooks/flow-hooks';
|
import { useFetchFlow } from '@/hooks/flow-hooks';
|
||||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
import { Button, Flex, Space } from 'antd';
|
import { Button, Flex, Space } from 'antd';
|
||||||
import { Link } from 'umi';
|
import { Link, useParams } from 'umi';
|
||||||
import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
|
import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
@ -15,6 +16,12 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
|
|||||||
const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
|
const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
|
||||||
const { data } = useFetchFlow();
|
const { data } = useFetchFlow();
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
|
const {
|
||||||
|
visible: overviewVisible,
|
||||||
|
hideModal: hideOverviewModal,
|
||||||
|
showModal: showOverviewModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -37,8 +44,19 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
|
|||||||
<Button type="primary" onClick={saveGraph}>
|
<Button type="primary" onClick={saveGraph}>
|
||||||
<b>{t('save')}</b>
|
<b>{t('save')}</b>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button type="primary" onClick={showOverviewModal}>
|
||||||
|
<b>{t('publish')}</b>
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{overviewVisible && (
|
||||||
|
<ChatOverviewModal
|
||||||
|
visible={overviewVisible}
|
||||||
|
hideModal={hideOverviewModal}
|
||||||
|
id={id!}
|
||||||
|
idKey="canvasId"
|
||||||
|
></ChatOverviewModal>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user