This commit is contained in:
Archer
2023-11-28 19:28:46 +08:00
committed by GitHub
parent e765c3bf95
commit a74e1d7166
75 changed files with 1139 additions and 417 deletions

View File

@@ -90,13 +90,15 @@ function App({ Component, pageProps }: AppProps) {
hiId && localStorage.setItem('inviterId', hiId);
}, [hiId]);
const title = feConfigs?.systemTitle || process.env.SYSTEM_NAME || '';
return (
<>
<Head>
<title>{feConfigs?.systemTitle || process.env.SYSTEM_NAME || ''}</title>
<title>{title}</title>
<meta
name="description"
content="FastGPT 是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!"
content={`${title} 是一个大模型应用编排系统,提供开箱即用的数据处理、模型调用等能力,可以快速的构建知识库并通过 Flow 可视化进行工作流编排,实现复杂的知识库场景!`}
/>
<meta
name="viewport"

View File

@@ -3,7 +3,6 @@ import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<title>{process.env.SYSTEM_NAME || 'FastGPT'}</title>
<Head />
<body>
<Main />

View File

@@ -152,6 +152,7 @@ async function init(limit: number): Promise<any> {
collectionId: data.collection_id,
q: data.q,
a: data.a,
fullTextToken: '',
indexes: [
{
defaultIndex: !data.a,

View File

@@ -0,0 +1,80 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { delay } from '@/utils/tools';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant';
import { ModuleDataTypeEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { ModuleItemType } from '@fastgpt/global/core/module/type';
let success = 0;
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { limit = 50 } = req.body as { limit: number };
await authCert({ req, authRoot: true });
await connectToDatabase();
success = 0;
console.log('total', await MongoApp.countDocuments());
await initApp(limit);
jsonRes(res, {
message: 'success'
});
} catch (error) {
console.log(error);
jsonRes(res, {
code: 500,
error
});
}
}
export async function initApp(limit = 50): Promise<any> {
try {
const apps = await MongoApp.find({ inited: false }).limit(limit);
if (apps.length === 0) return;
const result = await Promise.allSettled(
apps.map(async (app) => {
// 遍历app的modules找到 datasetSearch, 如果 rerank=true searchMode = embFullTextReRank, 否则等于embedding
const modules = JSON.parse(JSON.stringify(app.modules)) as ModuleItemType[];
modules.forEach((module) => {
if (module.flowType === FlowNodeTypeEnum.datasetSearchNode) {
module.inputs.forEach((input, i) => {
if (input.key === 'rerank') {
const val = !!input.value as boolean;
module.inputs.splice(i, 1, {
key: ModuleInputKeyEnum.datasetSearchMode,
type: FlowNodeInputTypeEnum.hidden,
label: 'core.dataset.search.Mode',
valueType: ModuleDataTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false,
value: val
? DatasetSearchModeEnum.embFullTextReRank
: DatasetSearchModeEnum.embedding
});
}
});
}
});
app.modules = modules;
app.inited = true;
await app.save();
})
);
success += result.filter((item) => item.status === 'fulfilled').length;
console.log(`success: ${success}`);
return initApp(limit);
} catch (error) {
console.log(error);
await delay(1000);
return initApp(limit);
}
}

View File

@@ -8,6 +8,7 @@ import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import type { ModuleItemType } from '@fastgpt/global/core/module/type';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -378,7 +379,7 @@ function datasetTemplate({
},
{
key: 'similarity',
value: 0.5,
value: 0.4,
type: FlowNodeInputTypeEnum.slider,
label: '相似度',
connected: true
@@ -403,13 +404,23 @@ function datasetTemplate({
connected: true
},
{
key: 'rerank',
type: FlowNodeInputTypeEnum.switch,
label: '结果重排',
description: '将召回的结果进行进一步重排,可增加召回率',
plusField: true,
connected: true,
value: true
key: 'searchMode',
type: 'hidden',
label: 'core.dataset.search.Mode',
valueType: 'string',
showTargetInApp: false,
showTargetInPlugin: false,
value: DatasetSearchModeEnum.embFullTextReRank,
connected: false
},
{
key: 'datasetParamsModal',
type: 'selectDatasetParamsModal',
label: '',
connected: false,
valueType: 'any',
showTargetInApp: false,
showTargetInPlugin: false
}
],
outputs: [

View File

@@ -312,13 +312,23 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
connected: true
},
{
key: 'rerank',
type: FlowNodeInputTypeEnum.switch,
label: '结果重排',
description: '将召回的结果进行进一步重排,可增加召回率',
plusField: true,
key: 'searchMode',
type: 'hidden',
label: 'core.dataset.search.Mode',
valueType: 'string',
showTargetInApp: false,
showTargetInPlugin: false,
value: formData.dataset.searchMode,
connected: false
},
{
key: 'datasetParamsModal',
type: 'selectDatasetParamsModal',
label: '',
connected: false,
value: formData.dataset.rerank
valueType: 'any',
showTargetInApp: false,
showTargetInPlugin: false
}
],
outputs: [

View File

@@ -4,7 +4,6 @@ import { connectToDatabase } from '@/service/mongo';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import type { AppUpdateParams } from '@fastgpt/global/core/app/api';
import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
/* 获取我的模型 */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {

View File

@@ -67,8 +67,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
'limit.exportKbTime': new Date()
});
});
cursor.on('error', (err) => {
addLog.error(`export dataset error`, err);
res.status(500);
res.end();
});
} catch (err) {
res.status(500);
addLog.error(`export dataset error`, err);
jsonRes(res, {
code: 500,
error: err

View File

@@ -15,7 +15,7 @@ import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { datasetId, text, limit = 20, rerank } = req.body as SearchTestProps;
const { datasetId, text, limit = 20, searchMode } = req.body as SearchTestProps;
if (!datasetId || !text) {
throw new Error('缺少参数');
@@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
model: dataset.vectorModel,
limit: Math.min(limit, 50),
datasetIds: [datasetId],
rerank
searchMode
});
// push bill

View File

@@ -14,7 +14,8 @@ import {
defaultQGModels,
defaultVectorModels,
defaultAudioSpeechModels,
defaultWhisperModel
defaultWhisperModel,
defaultReRankModels
} from '@fastgpt/global/core/ai/model';
import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants';
import { getSimpleTemplatesFromPlus } from '@/service/core/app/utils';
@@ -31,11 +32,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
cqModels: global.cqModels,
extractModels: global.extractModels,
vectorModels: global.vectorModels,
audioSpeechModels: global.audioSpeechModels.map((item) => ({
reRankModels: global.reRankModels.map((item) => ({
...item,
baseUrl: undefined,
key: undefined
requestUrl: undefined,
requestAuth: undefined
})),
audioSpeechModels: global.audioSpeechModels,
priceMd: global.priceMd,
systemVersion: global.systemVersion || '0.0.0',
simpleModeTemplates: global.simpleModeTemplates
@@ -50,12 +52,11 @@ const defaultSystemEnv: SystemEnvType = {
};
const defaultFeConfigs: FeConfigsType = {
show_emptyChat: true,
show_contact: true,
show_git: true,
docUrl: 'https://docs.fastgpt.in',
show_register: false,
docUrl: 'https://doc.fastgpt.in',
openAPIDocUrl: 'https://doc.fastgpt.in/docs/development/openapi',
systemTitle: 'FastGPT',
authorText: 'Made by FastGPT Team.',
limit: {
exportLimitMinutes: 0
},
@@ -99,7 +100,14 @@ export function setDefaultData(res?: ConfigFileType) {
? { ...defaultSystemEnv, ...res.SystemParams }
: defaultSystemEnv;
global.feConfigs = res?.FeConfig
? { ...defaultFeConfigs, ...res.FeConfig, isPlus: !!res.SystemParams?.pluginBaseUrl }
? {
concatMd: res?.FeConfig?.show_git
? '* 项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n* 交流群: ![](https://doc.fastgpt.in/wechat-fastgpt.webp)'
: '',
...defaultFeConfigs,
...res.FeConfig,
isPlus: !!res.SystemParams?.pluginBaseUrl
}
: defaultFeConfigs;
global.chatModels = res?.ChatModels || defaultChatModels;
@@ -110,6 +118,8 @@ export function setDefaultData(res?: ConfigFileType) {
global.vectorModels = res?.VectorModels || defaultVectorModels;
global.reRankModels = res?.ReRankModels || defaultReRankModels;
global.audioSpeechModels = res?.AudioSpeechModels || defaultAudioSpeechModels;
global.whisperModel = res?.WhisperModel || defaultWhisperModel;

View File

@@ -15,7 +15,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
try {
let { input, model, billId } = req.body as Props;
await connectToDatabase();
const { teamId, tmbId } = await authCert({ req, authToken: true });
const { teamId, tmbId } = await authCert({ req, authToken: true, authApiKey: true });
if (!Array.isArray(input) || typeof input !== 'string') {
throw new Error('input is nor array or string');

View File

@@ -0,0 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { pushReRankBill } from '@/service/support/wallet/bill/push';
import { connectToDatabase } from '@/service/mongo';
import { authTeamBalance } from '@/service/support/permission/auth/bill';
import { PostReRankProps } from '@fastgpt/global/core/ai/api';
import { reRankRecall } from '@/service/core/ai/rerank';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
let { query, inputs } = req.body as PostReRankProps;
await connectToDatabase();
const { teamId, tmbId } = await authCert({
req,
authApiKey: true
});
await authTeamBalance(teamId);
// max 150 length
inputs = inputs.slice(0, 150);
const result = await reRankRecall({ query, inputs });
pushReRankBill({
teamId,
tmbId,
source: 'api'
});
jsonRes(res, {
data: result
});
} catch (err) {
console.log(err);
jsonRes(res, {
code: 500,
error: err
});
}
});

View File

@@ -33,7 +33,6 @@ import { AppSchema } from '@fastgpt/global/core/app/type.d';
import { delModelById } from '@/web/core/app/api';
import { useTranslation } from 'next-i18next';
import { getGuideModule } from '@fastgpt/global/core/module/utils';
import { DatasetParamsModal } from '@/components/core/module/DatasetSelectModal';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useAppStore } from '@/web/core/app/store/useAppStore';
@@ -56,6 +55,7 @@ import VariableEdit from '@/components/core/module/Flow/components/modules/Varia
const InfoModal = dynamic(() => import('../InfoModal'));
const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
const AIChatSettingsModal = dynamic(() => import('@/components/core/module/AIChatSettingsModal'));
function ConfigForm({
@@ -72,7 +72,6 @@ function ConfigForm({
const { appDetail, updateAppDetail } = useAppStore();
const { loadAllDatasets, allDatasets } = useDatasetStore();
const { isPc } = useSystemStore();
const [editVariable, setEditVariable] = useState<VariableItemType>();
const [refresh, setRefresh] = useState(false);
const { register, setValue, getValues, reset, handleSubmit, control } =
@@ -197,8 +196,8 @@ function ConfigForm({
})}
>
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
<MyTooltip label={'仅包含基础功能,复杂 agent 功能请使用高级编排。'} forceShow>
{t('core.app.App params config')}
<MyTooltip label={t('core.app.Simple Config Tip')} forceShow>
<QuestionOutlineIcon ml={2} fontSize={'md'} />
</MyTooltip>
</Box>
@@ -215,7 +214,7 @@ function ConfigForm({
}
}}
>
{isPc ? '保存并预览' : '保存'}
{isPc ? t('core.app.Save and preview') : t('common.Save')}
</Button>
</Flex>
@@ -248,7 +247,7 @@ function ConfigForm({
<Box {...BoxStyles} mt={2}>
<Flex alignItems={'center'}>
<Image alt={''} src={'/imgs/module/userGuide.png'} w={'18px'} />
<Box mx={2}></Box>
<Box mx={2}>{t('core.app.Welcome Text')}</Box>
<MyTooltip label={welcomeTextTip} forceShow>
<QuestionOutlineIcon />
</MyTooltip>
@@ -351,7 +350,7 @@ function ConfigForm({
</Flex>
)}
{(selectSimpleTemplate.systemForm.dataset.limit ||
selectSimpleTemplate.systemForm.dataset.rerank ||
selectSimpleTemplate.systemForm.dataset.searchMode ||
selectSimpleTemplate.systemForm.dataset.searchEmptyText ||
selectSimpleTemplate.systemForm.dataset.similarity) && (
<Flex alignItems={'center'} ml={3} {...BoxBtnStyles} onClick={onOpenKbParams}>
@@ -459,7 +458,7 @@ function ConfigForm({
<DatasetParamsModal
{...getValues('dataset')}
onClose={onCloseKbParams}
onChange={(e) => {
onSuccess={(e) => {
setValue('dataset', {
...getValues('dataset'),
...e

View File

@@ -66,12 +66,12 @@ const MyApps = () => {
);
/* 加载模型 */
useQuery(['loadApps'], () => loadMyApps(true), {
const { isFetching } = useQuery(['loadApps'], () => loadMyApps(true), {
refetchOnMount: true
});
return (
<PageContainer>
<PageContainer isLoading={isFetching}>
<Flex pt={3} px={5} alignItems={'center'}>
<Flex flex={1} alignItems={'center'}>
<Image src={'/imgs/module/ai.svg'} alt={''} mr={2} h={'24px'} />

View File

@@ -20,7 +20,7 @@ const Navbar = () => {
const { isOpen: isOpenMenu, onOpen: onOpenMenu, onClose: onCloseMenu } = useDisclosure();
const { isPc } = useSystemStore();
const menuList = [
...(feConfigs?.show_contact
...(feConfigs?.concatMd
? [
{
label: t('home.Commercial'),

View File

@@ -5,7 +5,10 @@ import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provide
import { useTranslation } from 'next-i18next';
const fileExtension = '.csv';
const csvTemplate = `index,content\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`;
const csvTemplate = `index,content
"必填内容","可选内容。CSV 中请注意内容不能包含双引号,双引号是列分割符号"
"结合人工智能的演进历程,AIGC的发展大致可以分为三个阶段即:早期萌芽阶段(20世纪50年代至90年代中期)、沉淀积累阶段(20世纪90年代中期至21世纪10年代中期),以及快速发展展阶段(21世纪10年代中期至今)。",""
"AIGC发展分为几个阶段","早期萌芽阶段(20世纪50年代至90年代中期)、沉淀积累阶段(20世纪90年代中期至21世纪10年代中期)、快速发展展阶段(21世纪10年代中期至今)"`;
const CsvImport = () => {
const { t } = useTranslation();

View File

@@ -1,5 +1,15 @@
import React, { useEffect, useMemo, useState } from 'react';
import { Box, Textarea, Button, Flex, useTheme, Grid, Progress, Switch } from '@chakra-ui/react';
import {
Box,
Textarea,
Button,
Flex,
useTheme,
Grid,
Progress,
Switch,
useDisclosure
} from '@chakra-ui/react';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest';
import { getDatasetDataItemById, postSearchText } from '@/web/core/dataset/api';
@@ -13,12 +23,14 @@ import { useToast } from '@/web/common/hooks/useToast';
import { customAlphabet } from 'nanoid';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { useTranslation } from 'next-i18next';
import { feConfigs } from '@/web/common/system/staticData';
import { SearchTestResponse } from '../../../../global/core/dataset/api';
import { SearchTestResponse } from '@/global/core/dataset/api';
import { DatasetSearchModeEnum, DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant';
import dynamic from 'next/dynamic';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal'));
const Test = ({ datasetId }: { datasetId: string }) => {
const { t } = useTranslation();
const theme = useTheme();
@@ -30,15 +42,24 @@ const Test = ({ datasetId }: { datasetId: string }) => {
const [inputText, setInputText] = useState('');
const [datasetTestItem, setDatasetTestItem] = useState<SearchTestStoreItemType>();
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
const [rerank, setRerank] = useState(false);
const [searchMode, setSearchMode] = useState<`${DatasetSearchModeEnum}`>(
DatasetSearchModeEnum.embedding
);
const searchModeData = DatasetSearchModeMap[searchMode];
const kbTestHistory = useMemo(
const {
isOpen: isOpenSelectMode,
onOpen: onOpenSelectMode,
onClose: onCloseSelectMode
} = useDisclosure();
const testHistories = useMemo(
() => datasetTestList.filter((item) => item.datasetId === datasetId),
[datasetId, datasetTestList]
);
const { mutate, isLoading } = useRequest({
mutationFn: () => postSearchText({ datasetId, text: inputText.trim(), rerank, limit: 30 }),
mutationFn: () => postSearchText({ datasetId, text: inputText.trim(), searchMode, limit: 30 }),
onSuccess(res: SearchTestResponse) {
if (!res || res.list.length === 0) {
return toast({
@@ -71,6 +92,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
return (
<Box h={'100%'} display={['block', 'flex']}>
{/* input */}
<Box
h={['auto', '100%']}
display={['block', 'flex']}
@@ -86,12 +108,14 @@ const Test = ({ datasetId }: { datasetId: string }) => {
<MyIcon mr={2} name={'text'} w={'18px'} h={'18px'} color={'myBlue.700'} />
{t('core.dataset.test.Test Text')}
</Box>
{feConfigs?.isPlus && (
<Flex alignItems={'center'}>
{t('dataset.recall.rerank')}
<Switch ml={1} isChecked={rerank} onChange={(e) => setRerank(e.target.checked)} />
</Flex>
)}
<Button
variant={'base'}
leftIcon={<MyIcon name={searchModeData.icon as any} w={'14px'} />}
size={'sm'}
onClick={onOpenSelectMode}
>
{t(searchModeData.title)}
</Button>
</Flex>
<Textarea
rows={6}
@@ -122,7 +146,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
<Box w={'80px'}>{t('common.Time')}</Box>
<Box w={'14px'}></Box>
</Flex>
{kbTestHistory.map((item) => (
{testHistories.map((item) => (
<Flex
key={item.id}
p={1}
@@ -162,6 +186,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
</Box>
</Box>
</Box>
{/* result show */}
<Box p={4} h={['auto', '100%']} overflow={'overlay'} flex={1}>
{!datasetTestItem?.results || datasetTestItem.results.length === 0 ? (
<Flex
@@ -307,6 +332,15 @@ const Test = ({ datasetId }: { datasetId: string }) => {
}}
/>
)}
{isOpenSelectMode && (
<DatasetParamsModal
searchMode={searchMode}
onClose={onCloseSelectMode}
onSuccess={(e) => {
setSearchMode(e.searchMode);
}}
/>
)}
</Box>
);
};

View File

@@ -11,7 +11,6 @@ import { feConfigs } from '@/web/common/system/staticData';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@/components/Icon';
import { customAlphabet } from 'nanoid';
import { useUserStore } from '@/web/support/user/useUserStore';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
interface Props {
@@ -86,6 +85,8 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
: [])
];
const isCommunityVersion = feConfigs?.show_register === false && feConfigs?.show_git;
return (
<>
<Box fontWeight={'bold'} fontSize={'2xl'} textAlign={'center'}>
@@ -94,7 +95,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
<form onSubmit={handleSubmit(onclickLogin)}>
<FormControl mt={8} isInvalid={!!errors.username}>
<Input
placeholder="邮箱/手机号/用户名"
placeholder={isCommunityVersion ? '使用root用户登录' : '邮箱/手机号/用户名'}
size={['md', 'lg']}
{...register('username', {
required: '邮箱/手机号/用户名不能为空'
@@ -108,7 +109,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
<Input
type={'password'}
size={['md', 'lg']}
placeholder="密码"
placeholder={isCommunityVersion ? 'root密码为你设置的环境变量' : '密码'}
{...register('password', {
required: '密码不能为空',
maxLength: {

View File

@@ -13,7 +13,6 @@ import { clearToken, setToken } from '@/web/support/user/auth';
import { feConfigs } from '@/web/common/system/staticData';
import CommunityModal from '@/components/CommunityModal';
import Script from 'next/script';
import { loginOut } from '@/web/support/user/api';
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
@@ -114,10 +113,10 @@ const Login = () => {
>
<DynamicComponent type={pageType} />
{feConfigs?.show_contact && (
{feConfigs?.concatMd && (
<Box
fontSize={'sm'}
color={'myGray.600'}
fontWeight={'bold'}
color={'myBlue.700'}
cursor={'pointer'}
position={'absolute'}
right={5}