Add question guide config (#3403)

* feat:Prompt task (#3337)

* feat:猜你想问自定义功能

* 修改用户输入框部分,去除冗余代码

* 删除不必要的属性

* 删除多余内容

* 修正了格式问题,并实现获取调试和app最新参数

* 修正了几行代码

* feat:Prompt task (#3337)

* feat:猜你想问自定义功能

* 修改用户输入框部分,去除冗余代码

* 删除不必要的属性

* 删除多余内容

* 修正了格式问题,并实现获取调试和app最新参数

* 修正了几行代码

* perf: question gudide code

* fix: i18n

* hunyuan logo

* fix: cq templates

* perf: create question guide code

* udpate svg

---------

Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
This commit is contained in:
Archer
2024-12-16 13:49:31 +08:00
committed by GitHub
parent 76d20b2b76
commit bfac393ab1
50 changed files with 775 additions and 397 deletions

View File

@@ -0,0 +1,194 @@
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { Box, Button, Flex, ModalBody, useDisclosure, Switch, BoxProps } from '@chakra-ui/react';
import React from 'react';
import { useTranslation } from 'next-i18next';
import type { AppQGConfigType } from '@fastgpt/global/core/app/type.d';
import MyModal from '@fastgpt/web/components/common/MyModal';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { defaultQGConfig } from '@fastgpt/global/core/app/constants';
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import AIModelSelector from '@/components/Select/AIModelSelector';
import CustomPromptEditor from '@fastgpt/web/components/common/Textarea/CustomPromptEditor';
import {
PROMPT_QUESTION_GUIDE,
PROMPT_QUESTION_GUIDE_FOOTER
} from '@fastgpt/global/core/ai/prompt/agent';
// question generator config
const QGConfig = ({
value = defaultQGConfig,
onChange
}: {
value?: AppQGConfigType;
onChange: (e: AppQGConfigType) => void;
}) => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
const isOpenQG = value.open;
const formLabel = isOpenQG
? t('common:core.app.whisper.Open')
: t('common:core.app.whisper.Close');
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/chat/QGFill'} mr={2} w={'20px'} />
<FormLabel>{t('common:core.app.Question Guide')}</FormLabel>
<ChatFunctionTip type={'nextQuestion'} />
<Box flex={1} />
<MyTooltip label={t('app:config_question_guide')}>
<Button
variant={'transparentBase'}
size={'sm'}
mr={'-5px'}
color={'myGray.600'}
onClick={onOpen}
>
{formLabel}
</Button>
</MyTooltip>
{isOpen && <QGConfigModal value={value} onChange={onChange} onClose={onClose} />}
</Flex>
);
};
export default QGConfig;
const LabelStyles: BoxProps = {
display: 'flex',
alignItems: 'center',
fontSize: 'sm',
color: 'myGray.900',
width: ['6rem', '8rem']
};
const QGConfigModal = ({
value,
onClose,
onChange
}: {
value: AppQGConfigType;
onChange: (e: AppQGConfigType) => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { llmModelList } = useSystemStore();
const customPrompt = value.customPrompt;
const isOpenQG = value.open;
const model = value?.model || llmModelList?.[0]?.model;
const {
isOpen: isOpenCustomPrompt,
onOpen: onOpenCustomPrompt,
onClose: onCloseCustomPrompt
} = useDisclosure();
return (
<>
<MyModal
title={t('common:core.chat.Question Guide')}
iconSrc="core/chat/QGFill"
isOpen
onClose={onClose}
width="500px"
>
<ModalBody px={[5, 10]} py={[4, 8]} pb={[4, 12]}>
<Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel flex={'0 0 100px'}>{t('app:core.app.QG.Switch')}</FormLabel>
<Switch
isChecked={isOpenQG}
onChange={(e) => {
onChange({
...value,
open: e.target.checked
});
}}
/>
</Flex>
{isOpenQG && (
<>
<Flex alignItems={'center'} mt={4}>
<Box {...LabelStyles} mr={2}>
{t('common:core.ai.Model')}
</Box>
<Box flex={'1 0 0'}>
<AIModelSelector
width={'100%'}
value={model}
list={llmModelList.map((item) => ({
value: item.model,
label: item.name
}))}
onchange={(e) => {
onChange({
...value,
model: e
});
}}
/>
</Box>
</Flex>
<Box mt={4}>
<Flex alignItems={'center'} mb={1}>
<FormLabel>{t('app:core.dataset.import.Custom prompt')}</FormLabel>
<QuestionTip ml={1} label={t('common:core.app.QG.Custom prompt tip')} />
<Box flex={1} />
<Button
size="xs"
variant={'transparentBase'}
leftIcon={<MyIcon name={'edit'} w={'14px'} />}
onClick={onOpenCustomPrompt}
>
{t('common:common.Edit')}
</Button>
</Flex>
<Box
position={'relative'}
bg={'myGray.50'}
border={'1px'}
borderColor={'borderColor.base'}
borderRadius={'md'}
maxH={'200px'}
overflow={'auto'}
px={3}
py={2}
fontSize={'sm'}
textAlign={'justify'}
whiteSpace={'pre-wrap'}
_hover={{
'& .mask': {
display: 'block'
}
}}
>
{customPrompt || PROMPT_QUESTION_GUIDE}
</Box>
</Box>
</>
)}
</ModalBody>
</MyModal>
{isOpenCustomPrompt && (
<CustomPromptEditor
defaultValue={customPrompt}
defaultPrompt={PROMPT_QUESTION_GUIDE}
footerPrompt={PROMPT_QUESTION_GUIDE_FOOTER}
onChange={(e) => {
onChange({
...value,
customPrompt: e
});
}}
onClose={onCloseCustomPrompt}
/>
)}
</>
);
};

View File

@@ -1,22 +0,0 @@
import MyIcon from '@fastgpt/web/components/common/Icon';
import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react';
import React from 'react';
import { useTranslation } from 'next-i18next';
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
// question generator switch
const QGSwitch = (props: SwitchProps) => {
const { t } = useTranslation();
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/chat/QGFill'} mr={2} w={'20px'} />
<FormLabel color={'myGray.600'}>{t('common:core.app.Question Guide')}</FormLabel>
<ChatFunctionTip type={'nextQuestion'} />
<Box flex={1} />
<Switch {...props} />
</Flex>
);
};
export default QGSwitch;

View File

@@ -29,7 +29,7 @@ const ChatFunctionTip = ({ type }: { type: `${FnTypeEnum}` }) => {
[FnTypeEnum.nextQuestion]: {
icon: '/imgs/app/nextQuestion-icon.svg',
title: t('common:core.app.Question Guide'),
desc: t('common:core.app.Question Guide Tip'),
desc: t('app:question_guide_tip'),
imgUrl: '/imgs/app/nextQuestion.svg'
},
[FnTypeEnum.tts]: {

View File

@@ -2,8 +2,8 @@ import React, { useState, useMemo, useCallback } from 'react';
import { useAudioPlay } from '@/web/common/utils/voice';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import {
AppAutoExecuteConfigType,
AppFileSelectConfigType,
AppQGConfigType,
AppTTSConfigType,
AppWhisperConfigType,
ChatInputGuideConfigType,
@@ -12,8 +12,8 @@ import {
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
import {
defaultAppSelectFileConfig,
defaultAutoExecuteConfig,
defaultChatInputGuideConfig,
defaultQGConfig,
defaultTTSConfig,
defaultWhisperConfig
} from '@fastgpt/global/core/app/constants';
@@ -37,7 +37,7 @@ type useChatStoreType = ChatProviderProps & {
welcomeText: string;
variableList: VariableItemType[];
allVariableList: VariableItemType[];
questionGuide: boolean;
questionGuide: AppQGConfigType;
ttsConfig: AppTTSConfigType;
whisperConfig: AppWhisperConfigType;
autoTTSResponse: boolean;
@@ -72,7 +72,11 @@ type useChatStoreType = ChatProviderProps & {
export const ChatBoxContext = createContext<useChatStoreType>({
welcomeText: '',
variableList: [],
questionGuide: false,
questionGuide: {
open: false,
model: undefined,
customPrompt: undefined
},
ttsConfig: {
type: 'none',
model: undefined,
@@ -143,10 +147,16 @@ const Provider = ({
ChatItemContext,
(v) => v.chatBoxData?.app?.chatConfig?.variables ?? []
);
const questionGuide = useContextSelector(
ChatItemContext,
(v) => v.chatBoxData?.app?.chatConfig?.questionGuide ?? false
);
const questionGuide = useContextSelector(ChatItemContext, (v) => {
const val = v.chatBoxData?.app?.chatConfig?.questionGuide;
if (typeof val === 'boolean') {
return {
...defaultQGConfig,
open: val
};
}
return v.chatBoxData?.app?.chatConfig?.questionGuide ?? defaultQGConfig;
});
const ttsConfig = useContextSelector(
ChatItemContext,
(v) => v.chatBoxData?.app?.chatConfig?.ttsConfig ?? defaultTTSConfig

View File

@@ -335,7 +335,7 @@ const ChatBox = ({
// create question guide
const createQuestionGuide = useCallback(async () => {
if (!questionGuide || chatController.current?.signal?.aborted) return;
if (!questionGuide.open || chatController.current?.signal?.aborted) return;
try {
const abortSignal = new AbortController();
questionGuideController.current = abortSignal;
@@ -344,6 +344,7 @@ const ChatBox = ({
{
appId,
chatId,
questionGuide,
...outLinkAuthData
},
abortSignal
@@ -355,7 +356,7 @@ const ChatBox = ({
}, 100);
}
} catch (error) {}
}, [questionGuide, appId, outLinkAuthData, scrollToBottom]);
}, [questionGuide, appId, chatId, outLinkAuthData, scrollToBottom]);
/* Abort chat completions, questionGuide */
const abortRequest = useMemoizedFn((signal: string = 'stop') => {