* Milvus (#1644)

* feat: support regx

* 4.8.3 test and fix (#1648)

* perf: version tip

* feat: sandbox support log

* fix: debug component render

* fix: share page header

* fix: input guide auth

* fix: iso viewport

* remove file

* fix: route url

* feat: add debug timout

* perf: reference select support trigger

* perf: session code

* perf: theme

* perf: load milvus
This commit is contained in:
Archer
2024-06-01 09:26:11 +08:00
committed by GitHub
parent 9fc6a8c74a
commit a259d034b8
81 changed files with 1775 additions and 594 deletions

View File

@@ -1,12 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
import { NextAPI } from '@/service/middleware/entry';
import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants';
import { connectionMongo } from '@fastgpt/service/common/mongo';
import { addLog } from '@fastgpt/service/common/system/log';
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
async function handler(req: NextApiRequest, res: NextApiResponse) {

View File

@@ -1,41 +0,0 @@
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { NextApiRequest, NextApiResponse } from 'next';
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
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 MongoChatInputGuide.find({ appId, teamId });
const currentTexts = currentQGuide.map((item) => item.text);
const textsToDelete = currentTexts.filter((text) => !textList.includes(text));
await MongoChatInputGuide.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 MongoChatInputGuide.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

@@ -2,21 +2,29 @@ import type { NextApiResponse } from 'next';
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { authChatCert } from '@/service/support/permission/auth/chat';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
export type QueryChatInputGuideProps = {
export type QueryChatInputGuideBody = OutLinkChatAuthProps & {
appId: string;
searchKey: string;
};
export type QueryChatInputGuideResponse = string[];
async function handler(
req: ApiRequestProps<{}, QueryChatInputGuideProps>,
req: ApiRequestProps<QueryChatInputGuideBody>,
res: NextApiResponse<any>
): Promise<QueryChatInputGuideResponse> {
const { appId, searchKey } = req.query;
const { appId, searchKey } = req.body;
await authApp({ req, appId, authToken: true, authApiKey: true, per: 'r' });
// tmp auth
const { teamId } = await authChatCert({ req, authToken: true });
const app = await MongoApp.findOne({ _id: appId, teamId });
if (!app) {
return Promise.reject(AppErrEnum.unAuthApp);
}
const params = {
appId,

View File

@@ -31,26 +31,17 @@ import { useI18n } from '@/web/context/I18n';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/web/core/app/context/appContext';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'), {
ssr: false
});
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'), {
ssr: false
});
const ToolSelectModal = dynamic(() => import('./ToolSelectModal'), { ssr: false });
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'), { ssr: false });
const QGSwitch = dynamic(() => import('@/components/core/app/QGSwitch'), { ssr: false });
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'), { ssr: false });
const InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig'), {
ssr: false
});
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
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 InputGuideConfig = dynamic(() => import('@/components/core/app/InputGuideConfig'));
const ScheduledTriggerConfig = dynamic(
() => import('@/components/core/app/ScheduledTriggerConfig'),
{ ssr: false }
() => import('@/components/core/app/ScheduledTriggerConfig')
);
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'), {
ssr: false
});
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'));
const BoxStyles: BoxProps = {
px: 5,

View File

@@ -15,16 +15,16 @@ const ChatHeader = ({
appName,
appAvatar,
chatModels,
appId,
showHistory,
onRoute2AppDetail,
onOpenSlider
}: {
history: ChatItemType[];
appName: string;
appAvatar: string;
chatModels?: string[];
appId?: string;
showHistory?: boolean;
onRoute2AppDetail?: () => void;
onOpenSlider: () => void;
}) => {
const router = useRouter();
@@ -80,13 +80,7 @@ const ChatHeader = ({
<Flex px={3} alignItems={'center'} flex={'1 0 0'} w={0} justifyContent={'center'}>
<Avatar src={appAvatar} w={'16px'} />
<Box
ml={1}
className="textEllipsis"
onClick={() => {
appId && router.push(`/app/detail?appId=${appId}`);
}}
>
<Box ml={1} className="textEllipsis" onClick={onRoute2AppDetail}>
{appName}
</Box>
</Flex>

View File

@@ -335,10 +335,10 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
<ChatHeader
appAvatar={chatData.app.avatar}
appName={chatData.app.name}
appId={appId}
history={chatData.history}
chatModels={chatData.app.chatModels}
onOpenSlider={onOpenSlider}
onRoute2AppDetail={() => router.push(`/app/detail?appId=${appId}`)}
showHistory
/>

View File

@@ -378,7 +378,6 @@ const OutLink = ({
history={chatData.history}
showHistory={showHistory === '1'}
onOpenSlider={onOpenSlider}
appId={chatData.appId}
/>
{/* chat box */}
<Box flex={1}>

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useRef, useState } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import {
Box,
Flex,
@@ -17,7 +17,7 @@ import {
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import LeftRadio from '@fastgpt/web/components/common/Radio/LeftRadio';
import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
import { TrainingModeEnum, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
import { ImportProcessWayEnum } from '@/web/core/dataset/constants';
import MyTooltip from '@/components/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
@@ -27,6 +27,7 @@ import Preview from '../components/Preview';
import Tag from '@fastgpt/web/components/common/Tag/index';
import { useContextSelector } from 'use-context-selector';
import { DatasetImportContext } from '../Context';
import { useToast } from '@fastgpt/web/hooks/useToast';
function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean }) {
const { t } = useTranslation();
@@ -42,8 +43,10 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
maxChunkSize,
priceTip
} = useContextSelector(DatasetImportContext, (v) => v);
const { getValues, setValue, register } = processParamsForm;
const [refresh, setRefresh] = useState(false);
const { getValues, setValue, register, watch } = processParamsForm;
const { toast } = useToast();
const mode = watch('mode');
const way = watch('way');
const {
isOpen: isOpenCustomPrompt,
@@ -53,12 +56,21 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
const trainingModeList = useMemo(() => {
const list = Object.entries(TrainingTypeMap);
return list;
}, []);
return list.filter(([key, value]) => {
if (feConfigs?.isPlus) return true;
return value.openSource;
});
}, [feConfigs?.isPlus]);
const onSelectTrainWay = useCallback(
(e: TrainingModeEnum) => {
if (!feConfigs?.isPlus && !TrainingTypeMap[e]?.openSource) {
return toast({
status: 'warning',
title: t('common.system.Commercial version function')
});
}
setValue('mode', e);
},
[feConfigs?.isPlus, setValue, t, toast]
);
return (
<Box h={'100%'} display={['block', 'flex']} gap={5}>
@@ -80,11 +92,8 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
}))}
px={3}
py={2}
value={getValues('mode')}
onChange={(e) => {
setValue('mode', e);
setRefresh(!refresh);
}}
value={mode}
onChange={onSelectTrainWay}
gridTemplateColumns={'repeat(3,1fr)'}
defaultBg="white"
activeBg="white"
@@ -105,7 +114,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
title: t('core.dataset.import.Custom process'),
desc: t('core.dataset.import.Custom process desc'),
value: ImportProcessWayEnum.custom,
children: getValues('way') === ImportProcessWayEnum.custom && (
children: way === ImportProcessWayEnum.custom && (
<Box mt={5}>
{showChunkInput && chunkSizeField && (
<Box>
@@ -250,11 +259,10 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
py={3}
defaultBg="white"
activeBg="white"
value={getValues('way')}
value={way}
w={'100%'}
onChange={(e) => {
setValue('way', e);
setRefresh(!refresh);
}}
></LeftRadio>
</Flex>
@@ -286,7 +294,6 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
defaultValue={getValues('qaPrompt')}
onChange={(e) => {
setValue('qaPrompt', e);
setRefresh(!refresh);
}}
onClose={onCloseCustomPrompt}
/>

View File

@@ -18,21 +18,19 @@ import MyRadio from '@/components/common/MyRadio';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MySelect from '@fastgpt/web/components/common/MySelect';
import AIModelSelector from '@/components/Select/AIModelSelector';
import { useI18n } from '@/web/context/I18n';
const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => {
const { t } = useTranslation();
const { datasetT } = useI18n();
const [refresh, setRefresh] = useState(false);
const { toast } = useToast();
const router = useRouter();
const { isPc, feConfigs, vectorModelList, datasetModelList } = useSystemStore();
const filterNotHiddenVectorModelList = vectorModelList.filter((item) => !item.hidden);
const { register, setValue, getValues, handleSubmit } = useForm<CreateDatasetParams>({
const { register, setValue, handleSubmit, watch } = useForm<CreateDatasetParams>({
defaultValues: {
parentId,
type: DatasetTypeEnum.dataset,
@@ -43,6 +41,10 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
agentModel: datasetModelList[0].model
}
});
const avatar = watch('avatar');
const datasetType = watch('type');
const vectorModel = watch('vectorModel');
const agentModel = watch('agentModel');
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png',
@@ -61,7 +63,6 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
maxH: 300
});
setValue('avatar', src);
setRefresh((state) => !state);
} catch (err: any) {
toast({
title: getErrText(err, t('common.avatar.Select Failed')),
@@ -85,6 +86,22 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
}
});
const onSelectDatasetType = useCallback(
(e: DatasetTypeEnum) => {
if (
!feConfigs?.isPlus &&
(e === DatasetTypeEnum.websiteDataset || e === DatasetTypeEnum.externalFile)
) {
return toast({
status: 'warning',
title: t('common.system.Commercial version function')
});
}
setValue('type', e);
},
[feConfigs?.isPlus, setValue, t, toast]
);
return (
<MyModal
iconSrc="/imgs/workflow/db.png"
@@ -109,28 +126,21 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
icon: 'core/dataset/commonDataset',
desc: datasetT('Common Dataset Desc')
},
...(feConfigs.isPlus
? [
{
title: datasetT('Website Dataset'),
value: DatasetTypeEnum.websiteDataset,
icon: 'core/dataset/websiteDataset',
desc: datasetT('Website Dataset Desc')
},
{
title: datasetT('External File'),
value: DatasetTypeEnum.externalFile,
icon: 'core/dataset/externalDataset',
desc: datasetT('External file Dataset Desc')
}
]
: [])
{
title: datasetT('Website Dataset'),
value: DatasetTypeEnum.websiteDataset,
icon: 'core/dataset/websiteDataset',
desc: datasetT('Website Dataset Desc')
},
{
title: datasetT('External File'),
value: DatasetTypeEnum.externalFile,
icon: 'core/dataset/externalDataset',
desc: datasetT('External file Dataset Desc')
}
]}
value={getValues('type')}
onChange={(e) => {
setValue('type', e as DatasetTypeEnum);
setRefresh(!refresh);
}}
value={datasetType}
onChange={onSelectDatasetType}
/>
</>
<Box mt={5}>
@@ -141,7 +151,7 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
<MyTooltip label={t('common.avatar.Select Avatar')}>
<Avatar
flexShrink={0}
src={getValues('avatar')}
src={avatar}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
@@ -173,14 +183,13 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
<Box flex={1}>
<AIModelSelector
w={'100%'}
value={getValues('vectorModel')}
value={vectorModel}
list={filterNotHiddenVectorModelList.map((item) => ({
label: item.name,
value: item.model
}))}
onchange={(e) => {
setValue('vectorModel', e);
setRefresh((state) => !state);
}}
/>
</Box>
@@ -192,14 +201,13 @@ const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: st
<Box flex={1}>
<AIModelSelector
w={'100%'}
value={getValues('agentModel')}
value={agentModel}
list={datasetModelList.map((item) => ({
label: item.name,
value: item.model
}))}
onchange={(e) => {
setValue('agentModel', e);
setRefresh((state) => !state);
}}
/>
</Box>

View File

@@ -1,6 +1,5 @@
import React, { useCallback, useEffect } from 'react';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { ResLogin } from '@/global/support/api/userRes.d';
import { useChatStore } from '@/web/core/chat/storeChat';
import { useUserStore } from '@/web/support/user/useUserStore';
@@ -9,7 +8,6 @@ import { postFastLogin } from '@/web/support/user/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import Loading from '@fastgpt/web/components/common/MyLoading';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useQuery } from '@tanstack/react-query';
import { getErrText } from '@fastgpt/global/common/error/utils';
const FastLogin = ({