mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-03 13:38:00 +00:00
v4.5.2 (#439)
This commit is contained in:
@@ -77,13 +77,7 @@ const QuoteModal = ({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ModalBody
|
||||
pt={0}
|
||||
whiteSpace={'pre-wrap'}
|
||||
textAlign={'justify'}
|
||||
wordBreak={'break-all'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} wordBreak={'break-all'}>
|
||||
{rawSearch.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
@@ -95,11 +89,18 @@ const QuoteModal = ({
|
||||
position={'relative'}
|
||||
overflow={'hidden'}
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
bg={i % 2 === 0 ? 'white' : 'myWhite.500'}
|
||||
>
|
||||
{!isShare && (
|
||||
<Flex alignItems={'flex-end'} mb={3} color={'myGray.500'}>
|
||||
<RawSourceText sourceName={item.sourceName} sourceId={item.sourceId} />
|
||||
<Box flex={1} />
|
||||
<Flex alignItems={'flex-end'} mb={3} fontSize={'sm'}>
|
||||
<RawSourceText
|
||||
fontWeight={'bold'}
|
||||
color={'black'}
|
||||
sourceName={item.sourceName}
|
||||
sourceId={item.sourceId}
|
||||
addr={!isShare}
|
||||
/>
|
||||
<Box flex={1} />
|
||||
{!isShare && (
|
||||
<Link
|
||||
as={NextLink}
|
||||
className="hover-data"
|
||||
@@ -111,13 +112,13 @@ const QuoteModal = ({
|
||||
{t('core.dataset.Go Dataset')}
|
||||
<MyIcon name={'rightArrowLight'} w={'10px'} />
|
||||
</Link>
|
||||
</Flex>
|
||||
)}
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Box color={'black'}>{item.q}</Box>
|
||||
<Box color={'black'}>{item.a}</Box>
|
||||
<Box color={'myGray.600'}>{item.a}</Box>
|
||||
{!isShare && (
|
||||
<Flex alignItems={'center'} mt={3} gap={4} color={'myGray.500'}>
|
||||
<Flex alignItems={'center'} fontSize={'sm'} mt={3} gap={4} color={'myGray.500'}>
|
||||
{isPc && (
|
||||
<MyTooltip label={t('core.dataset.data.id')}>
|
||||
<Flex border={theme.borders.base} px={3} borderRadius={'md'}>
|
||||
|
@@ -1,19 +1,22 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
|
||||
import { Flex, BoxProps, useDisclosure } from '@chakra-ui/react';
|
||||
import { Flex, BoxProps, useDisclosure, Image, useTheme } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Tag from '../Tag';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import ChatBoxDivider from '@/components/core/chat/Divider';
|
||||
|
||||
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
|
||||
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
|
||||
const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false });
|
||||
|
||||
const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemResType[] }) => {
|
||||
const theme = useTheme();
|
||||
const { isPc } = useSystemStore();
|
||||
const { t } = useTranslation();
|
||||
const [quoteModalData, setQuoteModalData] = useState<SearchDataResponseItemType[]>();
|
||||
@@ -27,18 +30,36 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
|
||||
const {
|
||||
chatAccount,
|
||||
quoteList = [],
|
||||
sourceList = [],
|
||||
historyPreview = [],
|
||||
runningTime = 0
|
||||
} = useMemo(() => {
|
||||
const chatData = responseData.find((item) => item.moduleType === FlowModuleTypeEnum.chatNode);
|
||||
const chatData = responseData.find((item) => item.moduleType === FlowNodeTypeEnum.chatNode);
|
||||
const quoteList = responseData
|
||||
.filter((item) => item.moduleType === FlowNodeTypeEnum.chatNode)
|
||||
.map((item) => item.quoteList)
|
||||
.flat()
|
||||
.filter((item) => item) as SearchDataResponseItemType[];
|
||||
const sourceList = quoteList.reduce(
|
||||
(acc: Record<string, SearchDataResponseItemType[]>, cur) => {
|
||||
if (!acc[cur.sourceName]) {
|
||||
acc[cur.sourceName] = [cur];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
chatAccount: responseData.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode)
|
||||
chatAccount: responseData.filter((item) => item.moduleType === FlowNodeTypeEnum.chatNode)
|
||||
.length,
|
||||
quoteList: responseData
|
||||
.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode)
|
||||
.map((item) => item.quoteList)
|
||||
quoteList,
|
||||
sourceList: Object.values(sourceList)
|
||||
.flat()
|
||||
.filter((item) => item) as SearchDataResponseItemType[],
|
||||
.map((item) => ({
|
||||
sourceName: item.sourceName,
|
||||
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName })
|
||||
})),
|
||||
historyPreview: chatData?.historyPreview,
|
||||
runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
|
||||
};
|
||||
@@ -50,64 +71,93 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
|
||||
};
|
||||
|
||||
return responseData.length === 0 ? null : (
|
||||
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
<Tag
|
||||
colorSchema="blue"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setQuoteModalData(quoteList)}
|
||||
>
|
||||
{quoteList.length}条引用
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{chatAccount === 1 && (
|
||||
<>
|
||||
{sourceList.length > 0 && (
|
||||
<>
|
||||
{historyPreview.length > 0 && (
|
||||
<MyTooltip label={'点击查看完整对话记录'}>
|
||||
<Tag
|
||||
colorSchema="green"
|
||||
<ChatBoxDivider icon="core/chat/quoteFill" text={t('chat.Quote')} />
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{sourceList.map((item) => (
|
||||
<Flex
|
||||
key={item.sourceName}
|
||||
alignItems={'center'}
|
||||
flexWrap={'wrap'}
|
||||
fontSize={'sm'}
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setContextModalData(historyPreview)}
|
||||
border={theme.borders.sm}
|
||||
py={1}
|
||||
px={2}
|
||||
borderRadius={'md'}
|
||||
_hover={{
|
||||
bg: 'myBlue.100'
|
||||
}}
|
||||
onClick={() => setQuoteModalData(quoteList)}
|
||||
>
|
||||
{historyPreview.length}条上下文
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
<Image src={item.icon} alt={''} mr={1} w={'12px'} />
|
||||
{item.sourceName}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{chatAccount > 1 && (
|
||||
<Tag colorSchema="blue" {...TagStyles}>
|
||||
多组 AI 对话
|
||||
</Tag>
|
||||
)}
|
||||
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
<Tag
|
||||
colorSchema="blue"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setQuoteModalData(quoteList)}
|
||||
>
|
||||
{quoteList.length}条引用
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{chatAccount === 1 && (
|
||||
<>
|
||||
{historyPreview.length > 0 && (
|
||||
<MyTooltip label={'点击查看完整对话记录'}>
|
||||
<Tag
|
||||
colorSchema="green"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setContextModalData(historyPreview)}
|
||||
>
|
||||
{historyPreview.length}条上下文
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{chatAccount > 1 && (
|
||||
<Tag colorSchema="blue" {...TagStyles}>
|
||||
多组 AI 对话
|
||||
</Tag>
|
||||
)}
|
||||
|
||||
{isPc && runningTime > 0 && (
|
||||
<MyTooltip label={'模块运行时间和'}>
|
||||
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
|
||||
{runningTime}s
|
||||
{isPc && runningTime > 0 && (
|
||||
<MyTooltip label={'模块运行时间和'}>
|
||||
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
|
||||
{runningTime}s
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
<MyTooltip label={'点击查看完整响应'}>
|
||||
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
|
||||
{t('chat.Complete Response')}
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
<MyTooltip label={'点击查看完整响应'}>
|
||||
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
|
||||
{t('chat.Complete Response')}
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal rawSearch={quoteModalData} onClose={() => setQuoteModalData(undefined)} />
|
||||
)}
|
||||
{!!contextModalData && (
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal response={responseData} onClose={onCloseWholeModal} />
|
||||
)}
|
||||
</Flex>
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal rawSearch={quoteModalData} onClose={() => setQuoteModalData(undefined)} />
|
||||
)}
|
||||
{!!contextModalData && (
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal response={responseData} onClose={onCloseWholeModal} />
|
||||
)}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -32,7 +32,7 @@ function Row({ label, value }: { label: string; value?: string | number | React.
|
||||
) : null;
|
||||
}
|
||||
|
||||
const ResponseModal = ({
|
||||
const WholeResponseModal = ({
|
||||
response,
|
||||
onClose
|
||||
}: {
|
||||
@@ -50,6 +50,7 @@ const ResponseModal = ({
|
||||
<Image
|
||||
mr={2}
|
||||
src={
|
||||
item.moduleLogo ||
|
||||
ModuleTemplatesFlat.find((template) => item.moduleType === template.flowType)?.logo
|
||||
}
|
||||
alt={''}
|
||||
@@ -192,10 +193,22 @@ const ResponseModal = ({
|
||||
}
|
||||
})()}
|
||||
/>
|
||||
|
||||
{/* plugin */}
|
||||
<Row
|
||||
label={t('chat.response.plugin output')}
|
||||
value={(() => {
|
||||
try {
|
||||
return JSON.stringify(activeModule?.pluginOutput, null, 2);
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
})()}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResponseModal;
|
||||
export default WholeResponseModal;
|
||||
|
@@ -35,7 +35,7 @@ import { feConfigs } from '@/web/common/system/staticData';
|
||||
import { eventBus } from '@/web/common/utils/eventbus';
|
||||
import { adaptChat2GptMessages } from '@/utils/common/adapt/message';
|
||||
import { useMarkdown } from '@/web/common/hooks/useMarkdown';
|
||||
import { AppModuleItemType } from '@/types/app';
|
||||
import { ModuleItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import { VariableInputEnum } from '@/constants/app';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import type { MessageItemType } from '@/types/core/chat/type';
|
||||
@@ -54,6 +54,7 @@ import Avatar from '@/components/Avatar';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import MySelect from '@/components/Select';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import ChatBoxDivider from '@/components/core/chat/Divider';
|
||||
import dynamic from 'next/dynamic';
|
||||
const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
|
||||
@@ -99,7 +100,7 @@ type Props = {
|
||||
showEmptyIntro?: boolean;
|
||||
appAvatar?: string;
|
||||
userAvatar?: string;
|
||||
userGuideModule?: AppModuleItemType;
|
||||
userGuideModule?: ModuleItemType;
|
||||
active?: boolean;
|
||||
onUpdateVariable?: (e: Record<string, any>) => void;
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<{
|
||||
@@ -488,7 +489,7 @@ const ChatBox = (
|
||||
|
||||
return {
|
||||
bg: colorMap[chatContent.status] || colorMap.loading,
|
||||
name: t(chatContent.moduleName || 'Running')
|
||||
name: t(chatContent.moduleName || 'common.Loading')
|
||||
};
|
||||
}, [chatHistory, isChatting, t]);
|
||||
/* style end */
|
||||
@@ -496,6 +497,7 @@ const ChatBox = (
|
||||
// page change and abort request
|
||||
useEffect(() => {
|
||||
isNewChatReplace.current = false;
|
||||
setQuestionGuide([]);
|
||||
return () => {
|
||||
chatController.current?.abort('leave');
|
||||
if (!isNewChatReplace.current) {
|
||||
@@ -750,38 +752,28 @@ const ChatBox = (
|
||||
{index === chatHistory.length - 1 &&
|
||||
!isChatting &&
|
||||
questionGuides.length > 0 && (
|
||||
<Flex
|
||||
mt={2}
|
||||
borderTop={theme.borders.sm}
|
||||
alignItems={'center'}
|
||||
flexWrap={'wrap'}
|
||||
>
|
||||
<Box
|
||||
color={'myGray.500'}
|
||||
mt={2}
|
||||
mr={2}
|
||||
fontSize={'sm'}
|
||||
fontStyle={'italic'}
|
||||
>
|
||||
{t('chat.Question Guide Tips')}
|
||||
</Box>
|
||||
{questionGuides.map((item) => (
|
||||
<Button
|
||||
mt={2}
|
||||
key={item}
|
||||
mr="2"
|
||||
borderRadius={'md'}
|
||||
variant={'outline'}
|
||||
colorScheme={'gray'}
|
||||
size={'xs'}
|
||||
onClick={() => {
|
||||
resetInputVal(item);
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
<Box mt={2}>
|
||||
<ChatBoxDivider
|
||||
icon="core/chat/QGFill"
|
||||
text={t('chat.Question Guide Tips')}
|
||||
/>
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{questionGuides.map((item) => (
|
||||
<Button
|
||||
key={item}
|
||||
borderRadius={'md'}
|
||||
variant={'outline'}
|
||||
colorScheme={'gray'}
|
||||
size={'xs'}
|
||||
onClick={() => {
|
||||
resetInputVal(item);
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
{/* admin mark content */}
|
||||
{showMarkIcon && item.adminFeedback && (
|
||||
|
Reference in New Issue
Block a user