feat: question guide (#1508)

* feat: question guide

* fix

* fix

* fix

* change interface

* fix
This commit is contained in:
heheer
2024-05-19 17:34:16 +08:00
committed by GitHub
parent fd31a0b763
commit e35ce2caa0
40 changed files with 1071 additions and 34 deletions

View File

@@ -6,6 +6,7 @@ import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
import { NextAPI } from '@/service/middleware/entry';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@@ -46,6 +47,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
},
{ session }
);
await MongoAppQGuide.deleteMany(
{
appId
},
{ session }
);
// delete app
await MongoApp.deleteOne(
{

View File

@@ -0,0 +1,41 @@
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { NextApiRequest, NextApiResponse } from 'next';
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
import axios from 'axios';
import { NextAPI } from '@/service/middleware/entry';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { textList = [], appId, customURL } = req.body;
if (!customURL) {
const { teamId } = await authUserNotVisitor({ req, authToken: true });
const currentQGuide = await MongoAppQGuide.find({ appId, teamId });
const currentTexts = currentQGuide.map((item) => item.text);
const textsToDelete = currentTexts.filter((text) => !textList.includes(text));
await MongoAppQGuide.deleteMany({ text: { $in: textsToDelete }, appId, teamId });
const newTexts = textList.filter((text: string) => !currentTexts.includes(text));
const newDocuments = newTexts.map((text: string) => ({
text: text,
appId: appId,
teamId: teamId
}));
await MongoAppQGuide.insertMany(newDocuments);
} else {
try {
const response = await axios.post(customURL, {
textList,
appId
});
res.status(200).json(response.data);
} catch (error) {
res.status(500).json({ error });
}
}
}
export default NextAPI(handler);

View File

@@ -0,0 +1,48 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { MongoAppQGuide } from '@fastgpt/service/core/app/qGuideSchema';
import axios from 'axios';
import { PaginationProps } from '@fastgpt/web/common/fetch/type';
import { NextAPI } from '@/service/middleware/entry';
type Props = PaginationProps<{
appId: string;
customURL: string;
searchKey: string;
}>;
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { appId, customURL, current, pageSize, searchKey } = req.query as unknown as Props;
if (!customURL) {
const [result, total] = await Promise.all([
MongoAppQGuide.find({
appId,
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } })
})
.sort({
time: -1
})
.skip((current - 1) * pageSize)
.limit(pageSize),
MongoAppQGuide.countDocuments({ appId })
]);
return {
list: result.map((item) => item.text) || [],
total
};
} else {
try {
const response = await axios.get(customURL as string, {
params: {
appid: appId
}
});
res.status(200).json(response.data);
} catch (error) {
res.status(500).json({ error });
}
}
}
export default NextAPI(handler);

View File

@@ -27,6 +27,9 @@ import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
import { useInterval, useUpdateEffect } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
import { importQuestionGuides } from '@/web/core/app/api';
import { getAppQGuideCustomURL, getNodesWithNoQGuide } from '@/web/core/app/utils';
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
const PublishHistories = dynamic(
@@ -139,8 +142,18 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
const data = await flowData2StoreDataAndCheck();
if (data) {
try {
const { questionGuideText } = splitGuideModule(getGuideModule(data.nodes));
await importQuestionGuides({
appId: app._id,
textList: questionGuideText.textList,
customURL: getAppQGuideCustomURL(app)
});
const newNodes = getNodesWithNoQGuide(data.nodes, questionGuideText);
await publishApp(app._id, {
...data,
nodes: newNodes,
type: AppTypeEnum.advanced,
//@ts-ignore
version: 'v2'

View File

@@ -28,7 +28,7 @@ const Render = ({ app, onClose }: Props) => {
useEffect(() => {
if (!isV2Workflow) return;
initData(JSON.parse(workflowStringData));
}, [isV2Workflow, initData, app._id]);
}, [isV2Workflow, initData, app._id, workflowStringData]);
useEffect(() => {
if (!isV2Workflow) {

View File

@@ -104,7 +104,7 @@ const ChatTest = ({
return () => {
wat.unsubscribe();
};
}, []);
}, [setWorkflowData, watch]);
return (
<Flex

View File

@@ -12,7 +12,11 @@ import { useTranslation } from 'next-i18next';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { form2AppWorkflow } from '@/web/core/app/utils';
import {
form2AppWorkflow,
getAppQGuideCustomURL,
getNodesWithNoQGuide
} from '@/web/core/app/utils';
import dynamic from 'next/dynamic';
import MyTooltip from '@/components/MyTooltip';
@@ -30,6 +34,7 @@ import { TTSTypeEnum } from '@/web/core/app/constants';
import { getSystemVariables } from '@/web/core/app/utils';
import { useUpdate } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
import { importQuestionGuides } from '@/web/core/app/api';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
@@ -37,6 +42,7 @@ const ToolSelectModal = dynamic(() => import('./ToolSelectModal'));
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'));
const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'));
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'));
const QGuidesConfigModal = dynamic(() => import('@/components/core/app/QGuidesConfig'));
const BoxStyles: BoxProps = {
px: 5,
@@ -64,7 +70,7 @@ const EditForm = ({
const { t } = useTranslation();
const { appT } = useI18n();
const { publishApp, appDetail } = useAppStore();
const { appDetail, publishApp } = useAppStore();
const { allDatasets } = useDatasetStore();
const { llmModelList } = useSystemStore();
@@ -103,7 +109,7 @@ const EditForm = ({
const datasetSearchSetting = watch('dataset');
const variables = watch('userGuide.variables');
const formatVariables = useMemo(
const formatVariables: any = useMemo(
() => formatEditorVariablePickerIcon([...getSystemVariables(t), ...variables]),
[t, variables]
);
@@ -112,6 +118,7 @@ const EditForm = ({
const whisperConfig = getValues('userGuide.whisper');
const postQuestionGuide = getValues('userGuide.questionGuide');
const selectedTools = watch('selectedTools');
const QGuidesConfig = watch('userGuide.questionGuideText');
const selectDatasets = useMemo(
() => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)),
@@ -125,10 +132,19 @@ const EditForm = ({
/* on save app */
const { mutate: onSubmitPublish, isLoading: isSaving } = useRequest({
mutationFn: async (data: AppSimpleEditFormType) => {
const questionGuideText = data.userGuide.questionGuideText;
await importQuestionGuides({
appId: appDetail._id,
textList: questionGuideText.textList,
customURL: getAppQGuideCustomURL(appDetail)
});
const { nodes, edges } = form2AppWorkflow(data);
const newNodes = getNodesWithNoQGuide(nodes, questionGuideText);
await publishApp(appDetail._id, {
nodes,
nodes: newNodes,
edges,
type: AppTypeEnum.simple
});
@@ -435,7 +451,7 @@ const EditForm = ({
</Box>
{/* question guide */}
<Box {...BoxStyles} borderBottom={'none'}>
<Box {...BoxStyles}>
<QGSwitch
isChecked={postQuestionGuide}
size={'lg'}
@@ -444,6 +460,16 @@ const EditForm = ({
}}
/>
</Box>
{/* question tips */}
<Box {...BoxStyles} borderBottom={'none'}>
<QGuidesConfigModal
value={QGuidesConfig}
onChange={(e) => {
setValue('userGuide.questionGuideText', e);
}}
/>
</Box>
</Box>
</Box>

View File

@@ -110,7 +110,6 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
maxW={['90vw', '700px']}
w={'700px'}
h={['90vh', '80vh']}
overflow={'none'}
>
{/* Header: row and search */}
<Box px={[3, 6]} pt={4} display={'flex'} justifyContent={'space-between'} w={'full'}>

View File

@@ -18,6 +18,7 @@ import { useAppStore } from '@/web/core/app/store/useAppStore';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
import { useI18n } from '@/web/context/I18n';
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
const FlowEdit = dynamic(() => import('./components/FlowEdit'), {
loading: () => <Loading />

View File

@@ -33,10 +33,15 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import { useUserStore } from '@/web/support/user/useUserStore';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
import {
checkChatSupportSelectFileByChatModels,
getAppQuestionGuidesByUserGuideModule
} from '@/web/core/chat/utils';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
const router = useRouter();

View File

@@ -20,7 +20,10 @@ import PageContainer from '@/components/PageContainer';
import ChatHeader from './components/ChatHeader';
import ChatHistorySlider from './components/ChatHistorySlider';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
import {
checkChatSupportSelectFileByChatModels,
getAppQuestionGuidesByUserGuideModule
} from '@/web/core/chat/utils';
import { useTranslation } from 'next-i18next';
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
@@ -31,6 +34,9 @@ import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
import { addLog } from '@fastgpt/service/common/system/log';
import { connectToDatabase } from '@/service/mongo';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
const OutLink = ({
appName,
@@ -378,6 +384,7 @@ const OutLink = ({
history={chatData.history}
showHistory={showHistory === '1'}
onOpenSlider={onOpenSlider}
appId={chatData.appId}
/>
{/* chat box */}
<Box flex={1}>

View File

@@ -21,7 +21,10 @@ import ChatHistorySlider from './components/ChatHistorySlider';
import ChatHeader from './components/ChatHeader';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
import {
checkChatSupportSelectFileByChatModels,
getAppQuestionGuidesByUserGuideModule
} from '@/web/core/chat/utils';
import { useChatStore } from '@/web/core/chat/storeChat';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
@@ -35,6 +38,9 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
import MyBox from '@fastgpt/web/components/common/MyBox';
import SliderApps from './components/SliderApps';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { getAppQGuideCustomURL } from '@/web/core/app/utils';
const OutLink = () => {
const { t } = useTranslation();