mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 09:44:47 +00:00
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:
194
projects/app/src/components/core/app/QGConfig.tsx
Normal file
194
projects/app/src/components/core/app/QGConfig.tsx
Normal 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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@@ -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;
|
@@ -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]: {
|
||||
|
@@ -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
|
||||
|
@@ -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') => {
|
||||
|
@@ -8,15 +8,20 @@ import { NextAPI } from '@/service/middleware/entry';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
|
||||
|
||||
export type CreateQuestionGuideParams = OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
questionGuide?: {
|
||||
open: boolean;
|
||||
model?: string;
|
||||
customPrompt?: string;
|
||||
};
|
||||
};
|
||||
|
||||
async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: NextApiResponse<any>) {
|
||||
const { appId, chatId } = req.body;
|
||||
|
||||
const { appId, chatId, questionGuide: inputQuestionGuide } = req.body;
|
||||
const [{ tmbId, teamId }] = await Promise.all([
|
||||
authChatCrud({
|
||||
req,
|
||||
@@ -27,6 +32,13 @@ async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: Nex
|
||||
]);
|
||||
|
||||
// Auth app and get questionGuide config
|
||||
const questionGuide = await (async () => {
|
||||
if (inputQuestionGuide) {
|
||||
return inputQuestionGuide;
|
||||
}
|
||||
const { chatConfig } = await getAppLatestVersion(appId);
|
||||
return chatConfig.questionGuide;
|
||||
})();
|
||||
|
||||
// Get histories
|
||||
const { histories } = await getChatItems({
|
||||
@@ -38,15 +50,12 @@ async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: Nex
|
||||
});
|
||||
const messages = chats2GPTMessages({ messages: histories, reserveId: false });
|
||||
|
||||
const qgModel = global.llmModels[0];
|
||||
const qgModel = questionGuide?.model || global.llmModels[0].model;
|
||||
|
||||
const { result, tokens } = await createQuestionGuide({
|
||||
messages,
|
||||
model: qgModel.model
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: result
|
||||
model: qgModel,
|
||||
customPrompt: questionGuide?.customPrompt
|
||||
});
|
||||
|
||||
pushQuestionGuideUsage({
|
||||
@@ -54,6 +63,8 @@ async function handler(req: ApiRequestProps<CreateQuestionGuideParams>, res: Nex
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
@@ -40,7 +40,7 @@ const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSe
|
||||
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
|
||||
const ToolSelectModal = dynamic(() => import('./components/ToolSelectModal'));
|
||||
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'));
|
||||
const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'));
|
||||
const QGConfig = dynamic(() => import('@/components/core/app/QGConfig'));
|
||||
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'));
|
||||
const InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig'));
|
||||
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'));
|
||||
@@ -425,14 +425,14 @@ const EditForm = ({
|
||||
|
||||
{/* question guide */}
|
||||
<Box {...BoxStyles}>
|
||||
<QGSwitch
|
||||
isChecked={appForm.chatConfig.questionGuide}
|
||||
<QGConfig
|
||||
value={appForm.chatConfig.questionGuide}
|
||||
onChange={(e) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
questionGuide: e.target.checked
|
||||
questionGuide: e
|
||||
}
|
||||
}));
|
||||
}}
|
||||
|
@@ -30,7 +30,7 @@ export const compareSimpleAppSnapshot = (
|
||||
{
|
||||
welcomeText: appForm1.chatConfig?.welcomeText || '',
|
||||
variables: appForm1.chatConfig?.variables || [],
|
||||
questionGuide: appForm1.chatConfig?.questionGuide || false,
|
||||
questionGuide: appForm1.chatConfig?.questionGuide || undefined,
|
||||
ttsConfig: appForm1.chatConfig?.ttsConfig || undefined,
|
||||
whisperConfig: appForm1.chatConfig?.whisperConfig || undefined,
|
||||
chatInputGuide: appForm1.chatConfig?.chatInputGuide || undefined,
|
||||
@@ -39,7 +39,7 @@ export const compareSimpleAppSnapshot = (
|
||||
{
|
||||
welcomeText: appForm2.chatConfig?.welcomeText || '',
|
||||
variables: appForm2.chatConfig?.variables || [],
|
||||
questionGuide: appForm2.chatConfig?.questionGuide || false,
|
||||
questionGuide: appForm2.chatConfig?.questionGuide || undefined,
|
||||
ttsConfig: appForm2.chatConfig?.ttsConfig || undefined,
|
||||
whisperConfig: appForm2.chatConfig?.whisperConfig || undefined,
|
||||
chatInputGuide: appForm2.chatConfig?.chatInputGuide || undefined,
|
||||
|
@@ -3,7 +3,7 @@ import { NodeProps } from 'reactflow';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
|
||||
import QGSwitch from '@/components/core/app/QGSwitch';
|
||||
import QGConfig from '@/components/core/app/QGConfig';
|
||||
import TTSSelect from '@/components/core/app/TTSSelect';
|
||||
import WhisperConfig from '@/components/core/app/WhisperConfig';
|
||||
import InputGuideConfig from '@/components/core/app/InputGuideConfig';
|
||||
@@ -13,7 +13,12 @@ import NodeCard from './render/NodeCard';
|
||||
import ScheduledTriggerConfig from '@/components/core/app/ScheduledTriggerConfig';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../context';
|
||||
import { AppChatConfigType, AppDetailType, VariableItemType } from '@fastgpt/global/core/app/type';
|
||||
import {
|
||||
AppChatConfigType,
|
||||
AppDetailType,
|
||||
AppQGConfigType,
|
||||
VariableItemType
|
||||
} from '@fastgpt/global/core/app/type';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import VariableEdit from '@/components/core/app/VariableEdit';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
@@ -149,17 +154,16 @@ function AutoExecute({ chatConfig: { autoExecute }, setAppDetail }: ComponentPro
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionGuide({ chatConfig: { questionGuide = false }, setAppDetail }: ComponentProps) {
|
||||
function QuestionGuide({ chatConfig: { questionGuide }, setAppDetail }: ComponentProps) {
|
||||
return (
|
||||
<QGSwitch
|
||||
isChecked={questionGuide}
|
||||
<QGConfig
|
||||
value={questionGuide}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
chatConfig: {
|
||||
...state.chatConfig,
|
||||
questionGuide: value
|
||||
questionGuide: e
|
||||
}
|
||||
}));
|
||||
}}
|
||||
|
@@ -142,7 +142,7 @@ export const AiPointsTable = () => {
|
||||
{whisperModel?.charsPointsPrice +
|
||||
t('common:support.wallet.subscription.point') +
|
||||
' / 60' +
|
||||
t('common:unit.minute')}
|
||||
t('common:unit.seconds')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
@@ -58,10 +58,12 @@ export const emptyTemplates: Record<
|
||||
},
|
||||
{
|
||||
key: 'questionGuide',
|
||||
valueType: WorkflowIOValueTypeEnum.boolean,
|
||||
valueType: WorkflowIOValueTypeEnum.object,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: 'core.app.Question Guide',
|
||||
value: false
|
||||
value: {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'tts',
|
||||
@@ -285,10 +287,12 @@ export const emptyTemplates: Record<
|
||||
},
|
||||
{
|
||||
key: 'questionGuide',
|
||||
valueType: 'boolean',
|
||||
valueType: 'any',
|
||||
renderTypeList: ['hidden'],
|
||||
label: 'core.app.Question Guide',
|
||||
value: false
|
||||
value: {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'tts',
|
||||
|
Reference in New Issue
Block a user