refactor: 使用类型导入简化代码结构,重构 AIResponseBox 组件
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
|||||||
HStack
|
HStack
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import {
|
import type {
|
||||||
AIChatItemValueItemType,
|
AIChatItemValueItemType,
|
||||||
ToolModuleResponseItemType,
|
ToolModuleResponseItemType,
|
||||||
UserChatItemValueItemType
|
UserChatItemValueItemType
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import {
|
import type {
|
||||||
InteractiveBasicType,
|
InteractiveBasicType,
|
||||||
UserInputInteractive,
|
UserInputInteractive,
|
||||||
UserSelectInteractive
|
UserSelectInteractive
|
||||||
@@ -28,14 +28,61 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||||
import {
|
import {
|
||||||
SelectOptionsComponent,
|
SelectOptionsComponent,
|
||||||
SelectOption,
|
type SelectOptionType,
|
||||||
FormInputComponent,
|
FormInputComponent,
|
||||||
FormItem
|
type FormItemType
|
||||||
} from './Form/FormComponents';
|
} from './Form/FormComponents';
|
||||||
|
|
||||||
const onSendPrompt = (e: { text: string; isInteractivePrompt: boolean }) =>
|
const onSendPrompt = (e: { text: string; isInteractivePrompt: boolean }) =>
|
||||||
eventBus.emit(EventNameEnum.sendQuestion, e);
|
eventBus.emit(EventNameEnum.sendQuestion, e);
|
||||||
|
|
||||||
|
const formatJsonString = (jsonString: string) => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(jsonString), null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
return jsonString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledAccordionItem = React.memo(function StyledAccordionItem({
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
||||||
|
{children}
|
||||||
|
</AccordionItem>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledAccordionButton = React.memo(function StyledAccordionButton({
|
||||||
|
children,
|
||||||
|
py = 0
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
py?: number | string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AccordionButton
|
||||||
|
w={'auto'}
|
||||||
|
bg={'white'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
borderWidth={'1px'}
|
||||||
|
borderColor={'myGray.200'}
|
||||||
|
boxShadow={'1'}
|
||||||
|
pl={3}
|
||||||
|
pr={2.5}
|
||||||
|
py={py}
|
||||||
|
_hover={{
|
||||||
|
bg: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccordionButton>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const RenderText = React.memo(function RenderText({
|
const RenderText = React.memo(function RenderText({
|
||||||
showAnimation,
|
showAnimation,
|
||||||
text
|
text
|
||||||
@@ -58,44 +105,20 @@ const RenderTool = React.memo(
|
|||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{tools.map((tool) => {
|
{tools.map((tool) => {
|
||||||
const toolParams = (() => {
|
const toolParams = formatJsonString(tool.params);
|
||||||
try {
|
const toolResponse = formatJsonString(tool.response);
|
||||||
return JSON.stringify(JSON.parse(tool.params), null, 2);
|
|
||||||
} catch (error) {
|
|
||||||
return tool.params;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
const toolResponse = (() => {
|
|
||||||
try {
|
|
||||||
return JSON.stringify(JSON.parse(tool.response), null, 2);
|
|
||||||
} catch (error) {
|
|
||||||
return tool.response;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion key={tool.id} allowToggle _notLast={{ mb: 2 }}>
|
<Accordion key={tool.id} allowToggle _notLast={{ mb: 2 }}>
|
||||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
<StyledAccordionItem>
|
||||||
<AccordionButton
|
<StyledAccordionButton>
|
||||||
w={'auto'}
|
|
||||||
bg={'white'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
borderWidth={'1px'}
|
|
||||||
borderColor={'myGray.200'}
|
|
||||||
boxShadow={'1'}
|
|
||||||
pl={3}
|
|
||||||
pr={2.5}
|
|
||||||
_hover={{
|
|
||||||
bg: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar src={tool.toolAvatar} w={'1.25rem'} h={'1.25rem'} borderRadius={'sm'} />
|
<Avatar src={tool.toolAvatar} w={'1.25rem'} h={'1.25rem'} borderRadius={'sm'} />
|
||||||
<Box mx={2} fontSize={'sm'} color={'myGray.900'}>
|
<Box mx={2} fontSize={'sm'} color={'myGray.900'}>
|
||||||
{tool.toolName}
|
{tool.toolName}
|
||||||
</Box>
|
</Box>
|
||||||
{showAnimation && !tool.response && <MyIcon name={'common/loading'} w={'14px'} />}
|
{showAnimation && !tool.response && <MyIcon name={'common/loading'} w={'14px'} />}
|
||||||
<AccordionIcon color={'myGray.600'} ml={5} />
|
<AccordionIcon color={'myGray.600'} ml={5} />
|
||||||
</AccordionButton>
|
</StyledAccordionButton>
|
||||||
<AccordionPanel
|
<AccordionPanel
|
||||||
py={0}
|
py={0}
|
||||||
px={0}
|
px={0}
|
||||||
@@ -120,7 +143,7 @@ ${toolResponse}`}
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</StyledAccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -144,21 +167,8 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion allowToggle defaultIndex={isLastResponseValue ? 0 : undefined}>
|
<Accordion allowToggle defaultIndex={isLastResponseValue ? 0 : undefined}>
|
||||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
<StyledAccordionItem>
|
||||||
<AccordionButton
|
<StyledAccordionButton py={1}>
|
||||||
w={'auto'}
|
|
||||||
bg={'white'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
borderWidth={'1px'}
|
|
||||||
borderColor={'myGray.200'}
|
|
||||||
boxShadow={'1'}
|
|
||||||
pl={3}
|
|
||||||
pr={2.5}
|
|
||||||
py={1}
|
|
||||||
_hover={{
|
|
||||||
bg: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HStack mr={2} spacing={1}>
|
<HStack mr={2} spacing={1}>
|
||||||
<MyIcon name={'core/chat/think'} w={'0.85rem'} />
|
<MyIcon name={'core/chat/think'} w={'0.85rem'} />
|
||||||
<Box fontSize={'sm'}>{t('chat:ai_reasoning')}</Box>
|
<Box fontSize={'sm'}>{t('chat:ai_reasoning')}</Box>
|
||||||
@@ -166,7 +176,7 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
|||||||
|
|
||||||
{showAnimation && <MyIcon name={'common/loading'} w={'0.85rem'} />}
|
{showAnimation && <MyIcon name={'common/loading'} w={'0.85rem'} />}
|
||||||
<AccordionIcon color={'myGray.600'} ml={5} />
|
<AccordionIcon color={'myGray.600'} ml={5} />
|
||||||
</AccordionButton>
|
</StyledAccordionButton>
|
||||||
<AccordionPanel
|
<AccordionPanel
|
||||||
py={0}
|
py={0}
|
||||||
pr={0}
|
pr={0}
|
||||||
@@ -178,7 +188,7 @@ const RenderResoningContent = React.memo(function RenderResoningContent({
|
|||||||
>
|
>
|
||||||
<Markdown source={content} showAnimation={showAnimation} />
|
<Markdown source={content} showAnimation={showAnimation} />
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</StyledAccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -190,7 +200,7 @@ const RenderUserSelectInteractive = React.memo(function RenderInteractive({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<SelectOptionsComponent
|
<SelectOptionsComponent
|
||||||
options={(interactive.params.userSelectOptions || []) as SelectOption[]}
|
options={(interactive.params.userSelectOptions || []) as SelectOptionType[]}
|
||||||
description={interactive.params.description}
|
description={interactive.params.description}
|
||||||
selectedValue={interactive.params.userSelectedVal}
|
selectedValue={interactive.params.userSelectedVal}
|
||||||
onSelectOption={(value: string) => {
|
onSelectOption={(value: string) => {
|
||||||
@@ -232,7 +242,7 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
|||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} gap={2} w={'250px'}>
|
<Flex flexDirection={'column'} gap={2} w={'250px'}>
|
||||||
<FormInputComponent
|
<FormInputComponent
|
||||||
inputForm={(interactive.params.inputForm || []) as FormItem[]}
|
inputForm={(interactive.params.inputForm || []) as FormItemType[]}
|
||||||
description={interactive.params.description}
|
description={interactive.params.description}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
isDisabled={interactive.params.submitted}
|
isDisabled={interactive.params.submitted}
|
||||||
@@ -244,6 +254,44 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getResponseRenderer = (
|
||||||
|
value: UserChatItemValueItemType | AIChatItemValueItemType,
|
||||||
|
isChatting: boolean,
|
||||||
|
isLastResponseValue: boolean
|
||||||
|
) => {
|
||||||
|
if (value.type === ChatItemValueTypeEnum.text && value.text) {
|
||||||
|
return (
|
||||||
|
<RenderText showAnimation={isChatting && isLastResponseValue} text={value.text.content} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.type === ChatItemValueTypeEnum.reasoning && value.reasoning) {
|
||||||
|
return (
|
||||||
|
<RenderResoningContent
|
||||||
|
isChatting={isChatting}
|
||||||
|
isLastResponseValue={isLastResponseValue}
|
||||||
|
content={value.reasoning.content}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.type === ChatItemValueTypeEnum.tool && value.tools) {
|
||||||
|
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {
|
||||||
|
if (value.interactive.type === 'userSelect') {
|
||||||
|
return <RenderUserSelectInteractive interactive={value.interactive} />;
|
||||||
|
}
|
||||||
|
if (value.interactive?.type === 'userInput') {
|
||||||
|
return <RenderUserFormInteractive interactive={value.interactive} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI响应框主组件
|
||||||
const AIResponseBox = React.memo(function AIResponseBox({
|
const AIResponseBox = React.memo(function AIResponseBox({
|
||||||
value,
|
value,
|
||||||
isLastResponseValue,
|
isLastResponseValue,
|
||||||
@@ -253,28 +301,7 @@ const AIResponseBox = React.memo(function AIResponseBox({
|
|||||||
isLastResponseValue: boolean;
|
isLastResponseValue: boolean;
|
||||||
isChatting: boolean;
|
isChatting: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (value.type === ChatItemValueTypeEnum.text && value.text)
|
return getResponseRenderer(value, isChatting, isLastResponseValue);
|
||||||
return (
|
|
||||||
<RenderText showAnimation={isChatting && isLastResponseValue} text={value.text.content} />
|
|
||||||
);
|
|
||||||
if (value.type === ChatItemValueTypeEnum.reasoning && value.reasoning)
|
|
||||||
return (
|
|
||||||
<RenderResoningContent
|
|
||||||
isChatting={isChatting}
|
|
||||||
isLastResponseValue={isLastResponseValue}
|
|
||||||
content={value.reasoning.content}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (value.type === ChatItemValueTypeEnum.tool && value.tools)
|
|
||||||
return <RenderTool showAnimation={isChatting} tools={value.tools} />;
|
|
||||||
if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) {
|
|
||||||
if (value.interactive.type === 'userSelect')
|
|
||||||
return <RenderUserSelectInteractive interactive={value.interactive} />;
|
|
||||||
if (value.interactive?.type === 'userInput')
|
|
||||||
return <RenderUserFormInteractive interactive={value.interactive} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default AIResponseBox;
|
export default AIResponseBox;
|
||||||
|
|||||||
Reference in New Issue
Block a user