mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 02:12:38 +00:00
feat: question guide (#1508)
* feat: question guide * fix * fix * fix * change interface * fix
This commit is contained in:
@@ -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(
|
||||
{
|
||||
|
41
projects/app/src/pages/api/core/app/questionGuides/import.ts
Normal file
41
projects/app/src/pages/api/core/app/questionGuides/import.ts
Normal 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);
|
48
projects/app/src/pages/api/core/app/questionGuides/list.ts
Normal file
48
projects/app/src/pages/api/core/app/questionGuides/list.ts
Normal 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);
|
@@ -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'
|
||||
|
@@ -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) {
|
||||
|
@@ -104,7 +104,7 @@ const ChatTest = ({
|
||||
return () => {
|
||||
wat.unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
}, [setWorkflowData, watch]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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'}>
|
||||
|
@@ -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 />
|
||||
|
@@ -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();
|
||||
|
@@ -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}>
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user