mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 02:12:38 +00:00
v4.6.1 (#497)
This commit is contained in:
@@ -12,6 +12,8 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { Types, connectionMongo } from '@fastgpt/service/common/mongo';
|
||||
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
import { getUserDefaultTeam } from '@fastgpt/service/support/user/team/controller';
|
||||
import { getGFSCollection } from '@fastgpt/service/common/file/gridfs/controller';
|
||||
|
||||
let success = 0;
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
@@ -22,9 +24,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
await connectToDatabase();
|
||||
success = 0;
|
||||
|
||||
jsonRes(res, {
|
||||
data: await init(limit)
|
||||
});
|
||||
await init(limit);
|
||||
await initCollectionFileTeam(limit);
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@@ -103,3 +106,71 @@ async function init(limit: number): Promise<any> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function initCollectionFileTeam(limit: number) {
|
||||
/* init user default Team */
|
||||
const DatasetFile = getGFSCollection('dataset');
|
||||
const matchWhere = {
|
||||
$or: [{ 'metadata.teamId': { $exists: false } }, { 'metadata.teamId': null }]
|
||||
};
|
||||
const uniqueUsersWithNoTeamId = await DatasetFile.aggregate([
|
||||
{
|
||||
$match: matchWhere
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$metadata.userId', // 按 metadata.userId 分组以去重
|
||||
userId: { $first: '$metadata.userId' } // 保留第一个出现的 userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0, // 不显示 _id 字段
|
||||
userId: 1 // 只显示 userId 字段
|
||||
}
|
||||
}
|
||||
]).toArray();
|
||||
const users = uniqueUsersWithNoTeamId;
|
||||
|
||||
console.log('un init total', users.length);
|
||||
// limit 组一次
|
||||
const userArr: any[][] = [];
|
||||
for (let i = 0; i < users.length; i += limit) {
|
||||
userArr.push(users.slice(i, i + limit));
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
for await (const item of userArr) {
|
||||
await Promise.all(item.map((item) => init(item.userId)));
|
||||
success += limit;
|
||||
console.log(success);
|
||||
}
|
||||
|
||||
async function init(userId: string): Promise<any> {
|
||||
try {
|
||||
const tmb = await getUserDefaultTeam({
|
||||
userId
|
||||
});
|
||||
|
||||
await DatasetFile.updateMany(
|
||||
{
|
||||
'metadata.userId': String(userId),
|
||||
...matchWhere
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'metadata.teamId': String(tmb.teamId),
|
||||
'metadata.tmbId': String(tmb.tmbId)
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error === 'team not exist' || error === 'tmbId or userId is required') {
|
||||
return;
|
||||
}
|
||||
console.log(error);
|
||||
await delay(1000);
|
||||
return init(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -264,7 +264,7 @@ async function initCollectionFileTeam(limit: number) {
|
||||
|
||||
await DatasetFile.updateMany(
|
||||
{
|
||||
userId,
|
||||
'metadata.userId': String(userId),
|
||||
...matchWhere
|
||||
},
|
||||
{
|
||||
|
@@ -60,33 +60,30 @@ import PermissionIconText from '@/components/support/permission/IconText';
|
||||
import QGSwitch from '../QGSwitch';
|
||||
import TTSSelect from '../TTSSelect';
|
||||
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils';
|
||||
import { useSticky } from '@/web/common/hooks/useSticky';
|
||||
|
||||
const VariableEditModal = dynamic(() => import('@/components/core/module/VariableEditModal'));
|
||||
const InfoModal = dynamic(() => import('../InfoModal'));
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal'));
|
||||
const AIChatSettingsModal = dynamic(() => import('@/components/core/module/AIChatSettingsModal'));
|
||||
|
||||
const Settings = ({ appId }: { appId: string }) => {
|
||||
function ConfigForm({
|
||||
divRef,
|
||||
isSticky
|
||||
}: {
|
||||
divRef: React.RefObject<HTMLDivElement>;
|
||||
isSticky: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { appDetail, updateAppDetail } = useAppStore();
|
||||
const { loadAllDatasets, allDatasets } = useDatasetStore();
|
||||
const { isPc } = useSystemStore();
|
||||
|
||||
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
||||
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const { openConfirm: openConfirmSave, ConfirmModal: ConfirmSaveModal } = useConfirm({
|
||||
content: t('app.Confirm Save App Tip'),
|
||||
bg: appDetail.type === AppTypeEnum.basic ? '' : 'red.600'
|
||||
});
|
||||
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
|
||||
content: t('app.Confirm Del App Tip')
|
||||
});
|
||||
const { register, setValue, getValues, reset, handleSubmit, control } = useForm<EditFormType>({
|
||||
defaultValues: getDefaultAppForm()
|
||||
});
|
||||
@@ -111,16 +108,21 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
onClose: onCloseAIChatSetting
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isOpenKbSelect,
|
||||
isOpen: isOpenDatasetSelect,
|
||||
onOpen: onOpenKbSelect,
|
||||
onClose: onCloseKbSelect
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isOpenKbParams,
|
||||
isOpen: isOpenDatasetParams,
|
||||
onOpen: onOpenKbParams,
|
||||
onClose: onCloseKbParams
|
||||
} = useDisclosure();
|
||||
|
||||
const { openConfirm: openConfirmSave, ConfirmModal: ConfirmSaveModal } = useConfirm({
|
||||
content: t('app.Confirm Save App Tip'),
|
||||
bg: appDetail.type === AppTypeEnum.basic ? '' : 'red.600'
|
||||
});
|
||||
|
||||
const chatModelSelectList = useMemo(() => {
|
||||
return chatModelList.map((item) => ({
|
||||
value: item.model,
|
||||
@@ -133,32 +135,6 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
[allDatasets, datasets]
|
||||
);
|
||||
|
||||
/* 点击删除 */
|
||||
const { mutate: handleDelModel, isLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
if (!appDetail) return null;
|
||||
await delModelById(appDetail._id);
|
||||
return 'success';
|
||||
},
|
||||
onSuccess(res) {
|
||||
if (!res) return;
|
||||
toast({
|
||||
title: t('common.Delete Success'),
|
||||
status: 'success'
|
||||
});
|
||||
router.replace(`/app/list`);
|
||||
},
|
||||
errorToast: t('common.Delete Failed')
|
||||
});
|
||||
|
||||
const appModule2Form = useCallback(() => {
|
||||
const formVal = appModules2Form(appDetail.modules);
|
||||
reset(formVal);
|
||||
setTimeout(() => {
|
||||
setRefresh((state) => !state);
|
||||
}, 100);
|
||||
}, [appDetail.modules, reset]);
|
||||
|
||||
const { mutate: onSubmitSave, isLoading: isSaving } = useRequest({
|
||||
mutationFn: async (data: EditFormType) => {
|
||||
const modules = appForm2Modules(data);
|
||||
@@ -173,12 +149,20 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
errorToast: t('common.Save Failed')
|
||||
});
|
||||
|
||||
const appModule2Form = useCallback(() => {
|
||||
const formVal = appModules2Form(appDetail.modules);
|
||||
reset(formVal);
|
||||
setTimeout(() => {
|
||||
setRefresh((state) => !state);
|
||||
}, 100);
|
||||
}, [appDetail.modules, reset]);
|
||||
|
||||
useQuery(['loadAllDatasets'], loadAllDatasets);
|
||||
|
||||
useEffect(() => {
|
||||
appModule2Form();
|
||||
}, [appModule2Form]);
|
||||
|
||||
useQuery(['loadAllDatasets'], loadAllDatasets);
|
||||
|
||||
const BoxStyles: BoxProps = {
|
||||
bg: 'myWhite.200',
|
||||
px: 4,
|
||||
@@ -202,110 +186,23 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
h={'100%'}
|
||||
borderRight={'1.5px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
p={4}
|
||||
pt={[0, 4]}
|
||||
pb={10}
|
||||
overflow={'overlay'}
|
||||
>
|
||||
<Flex alignItems={'flex-end'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
<PermissionIconText permission={appDetail.permission} />
|
||||
</Box>
|
||||
<Box ml={1} color={'myGray.500'} fontSize={'sm'}>
|
||||
(AppId:{' '}
|
||||
<Box as={'span'} userSelect={'all'}>
|
||||
{appId}
|
||||
</Box>
|
||||
)
|
||||
</Box>
|
||||
</Flex>
|
||||
{/* basic info */}
|
||||
<Box
|
||||
border={theme.borders.base}
|
||||
borderRadius={'lg'}
|
||||
mt={2}
|
||||
px={5}
|
||||
<Box mt={2}>
|
||||
{/* title */}
|
||||
<Flex
|
||||
ref={divRef}
|
||||
position={'sticky'}
|
||||
top={-4}
|
||||
bg={'white'}
|
||||
py={4}
|
||||
bg={'myBlue.100'}
|
||||
position={'relative'}
|
||||
justifyContent={'space-between'}
|
||||
alignItems={'center'}
|
||||
zIndex={10}
|
||||
px={4}
|
||||
{...(isSticky && {
|
||||
borderBottom: theme.borders.base,
|
||||
boxShadow: '0 2px 10px rgba(0,0,0,0.12)'
|
||||
})}
|
||||
>
|
||||
<Flex alignItems={'center'} py={2}>
|
||||
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
|
||||
<Box ml={3} fontWeight={'bold'} fontSize={'lg'}>
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
{appDetail.isOwner && (
|
||||
<IconButton
|
||||
className="delete"
|
||||
position={'absolute'}
|
||||
top={4}
|
||||
right={4}
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'delete'} w={'14px'} />}
|
||||
variant={'base'}
|
||||
borderRadius={'md'}
|
||||
aria-label={'delete'}
|
||||
_hover={{
|
||||
bg: 'myGray.100',
|
||||
color: 'red.600'
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
onClick={openConfirmDel(handleDelModel)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box
|
||||
flex={1}
|
||||
my={2}
|
||||
className={'textEllipsis3'}
|
||||
wordBreak={'break-all'}
|
||||
color={'myGray.600'}
|
||||
>
|
||||
{appDetail.intro || '快来给应用一个介绍~'}
|
||||
</Box>
|
||||
<Flex>
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
|
||||
onClick={() => router.push(`/chat?appId=${appId}`)}
|
||||
>
|
||||
对话
|
||||
</Button>
|
||||
<Button
|
||||
mx={3}
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'shareLight'} w={'16px'} />}
|
||||
onClick={() => {
|
||||
router.replace({
|
||||
query: {
|
||||
appId,
|
||||
currentTab: 'outLink'
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
外接
|
||||
</Button>
|
||||
{appDetail.isOwner && (
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'settingLight'} w={'16px'} />}
|
||||
onClick={() => setSettingAppInfo(appDetail)}
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<Flex mt={5} justifyContent={'space-between'} alignItems={'center'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
应用配置
|
||||
<MyTooltip label={'仅包含基础功能,复杂 agent 功能请使用高级编排。'} forceShow>
|
||||
@@ -329,215 +226,217 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{/* welcome */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/userGuide.png'} w={'18px'} />
|
||||
<Box mx={2}>对话开场白</Box>
|
||||
<MyTooltip label={welcomeTextTip} forceShow>
|
||||
<QuestionOutlineIcon />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Textarea
|
||||
mt={2}
|
||||
rows={5}
|
||||
placeholder={welcomeTextTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('guide.welcome.text')}
|
||||
/>
|
||||
</Box>
|
||||
{/* variable */}
|
||||
<Box mt={2} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
变量
|
||||
</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={() => setEditVariable(addVariable())}>
|
||||
+ 新增
|
||||
</Flex>
|
||||
</Flex>
|
||||
{variables.length > 0 && (
|
||||
<Box
|
||||
mt={2}
|
||||
borderRadius={'lg'}
|
||||
overflow={'hidden'}
|
||||
borderWidth={'1px'}
|
||||
borderBottom="none"
|
||||
>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>变量名</Th>
|
||||
<Th>变量 key</Th>
|
||||
<Th>必填</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{variables.map((item, index) => (
|
||||
<Tr key={item.id}>
|
||||
<Td>{item.label} </Td>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.required ? '✔' : ''}</Td>
|
||||
<Td>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setEditVariable(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => removeVariable(index)}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* ai */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
{t('app.AI Settings')}
|
||||
</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={onOpenAIChatSetting}>
|
||||
<MyIcon mr={1} name={'settingLight'} w={'14px'} />
|
||||
{t('app.Open AI Advanced Settings')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>{t('core.ai.Model')}</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
value={getValues('chatModel.model')}
|
||||
list={chatModelSelectList}
|
||||
onchange={(val: any) => {
|
||||
setValue('chatModel.model', val);
|
||||
const maxToken =
|
||||
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
||||
?.maxResponse || 4000;
|
||||
const token = maxToken / 2;
|
||||
setValue('chatModel.maxToken', token);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
{t('core.ai.Prompt')}
|
||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
<Box px={4}>
|
||||
{/* welcome */}
|
||||
<Box {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/userGuide.png'} w={'18px'} />
|
||||
<Box mx={2}>对话开场白</Box>
|
||||
<MyTooltip label={welcomeTextTip} forceShow>
|
||||
<QuestionOutlineIcon />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Textarea
|
||||
mt={2}
|
||||
rows={5}
|
||||
placeholder={ChatModelSystemTip}
|
||||
placeholder={welcomeTextTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.systemPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
</Box>
|
||||
{...register('guide.welcome.text')}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* dataset */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<Box ml={2}>{t('core.dataset.Choose Dataset')}</Box>
|
||||
{/* variable */}
|
||||
<Box mt={2} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
变量
|
||||
</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={() => setEditVariable(addVariable())}>
|
||||
+ 新增
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||
<SmallAddIcon />
|
||||
{t('common.Choose')}
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||
{t('common.Params')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||
{t('core.dataset.Similarity')}: {getValues('dataset.searchSimilarity')},{' '}
|
||||
{t('core.dataset.Search Top K')}: {getValues('dataset.searchLimit')}
|
||||
{getValues('dataset.searchEmptyText') === ''
|
||||
? ''
|
||||
: t('core.dataset.Set Empty Result Tip')}
|
||||
</Flex>
|
||||
<Grid
|
||||
gridTemplateColumns={['repeat(2, minmax(0, 1fr))', 'repeat(3, minmax(0, 1fr))']}
|
||||
my={2}
|
||||
gridGap={[2, 4]}
|
||||
>
|
||||
{selectDatasets.map((item) => (
|
||||
<MyTooltip key={item._id} label={t('core.dataset.Read Dataset')}>
|
||||
<Flex
|
||||
overflow={'hidden'}
|
||||
alignItems={'center'}
|
||||
p={2}
|
||||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
cursor={'pointer'}
|
||||
onClick={() =>
|
||||
router.push({
|
||||
pathname: '/dataset/detail',
|
||||
query: {
|
||||
datasetId: item._id
|
||||
}
|
||||
})
|
||||
}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
||||
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
{variables.length > 0 && (
|
||||
<Box
|
||||
mt={2}
|
||||
borderRadius={'lg'}
|
||||
overflow={'hidden'}
|
||||
borderWidth={'1px'}
|
||||
borderBottom="none"
|
||||
>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>变量名</Th>
|
||||
<Th>变量 key</Th>
|
||||
<Th>必填</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{variables.map((item, index) => (
|
||||
<Tr key={item.id}>
|
||||
<Td>{item.label} </Td>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.required ? '✔' : ''}</Td>
|
||||
<Td>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setEditVariable(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => removeVariable(index)}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<TTSSelect
|
||||
value={getValues('tts')}
|
||||
onChange={(e) => {
|
||||
setValue('tts', e);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* ai */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
{t('app.AI Settings')}
|
||||
</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={onOpenAIChatSetting}>
|
||||
<MyIcon mr={1} name={'settingLight'} w={'14px'} />
|
||||
{t('app.Open AI Advanced Settings')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<QGSwitch
|
||||
isChecked={getValues('questionGuide')}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
setValue('questionGuide', value);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>{t('core.ai.Model')}</Box>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
value={getValues('chatModel.model')}
|
||||
list={chatModelSelectList}
|
||||
onchange={(val: any) => {
|
||||
setValue('chatModel.model', val);
|
||||
const maxToken =
|
||||
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
||||
?.maxResponse || 4000;
|
||||
const token = maxToken / 2;
|
||||
setValue('chatModel.maxToken', token);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
{t('core.ai.Prompt')}
|
||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Textarea
|
||||
rows={5}
|
||||
minH={'60px'}
|
||||
placeholder={ChatModelSystemTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.systemPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* dataset */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<Box ml={2}>{t('core.dataset.Choose Dataset')}</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||
<SmallAddIcon />
|
||||
{t('common.Choose')}
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||
{t('common.Params')}
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||
{t('core.dataset.Similarity')}: {getValues('dataset.searchSimilarity')},{' '}
|
||||
{t('core.dataset.Search Top K')}: {getValues('dataset.searchLimit')}
|
||||
{getValues('dataset.searchEmptyText') === ''
|
||||
? ''
|
||||
: t('core.dataset.Set Empty Result Tip')}
|
||||
</Flex>
|
||||
<Grid
|
||||
gridTemplateColumns={['repeat(2, minmax(0, 1fr))', 'repeat(3, minmax(0, 1fr))']}
|
||||
my={2}
|
||||
gridGap={[2, 4]}
|
||||
>
|
||||
{selectDatasets.map((item) => (
|
||||
<MyTooltip key={item._id} label={t('core.dataset.Read Dataset')}>
|
||||
<Flex
|
||||
overflow={'hidden'}
|
||||
alignItems={'center'}
|
||||
p={2}
|
||||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
cursor={'pointer'}
|
||||
onClick={() =>
|
||||
router.push({
|
||||
pathname: '/dataset/detail',
|
||||
query: {
|
||||
datasetId: item._id
|
||||
}
|
||||
})
|
||||
}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
||||
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* tts */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<TTSSelect
|
||||
value={getValues('tts')}
|
||||
onChange={(e) => {
|
||||
setValue('tts', e);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* whisper */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<QGSwitch
|
||||
isChecked={getValues('questionGuide')}
|
||||
size={'lg'}
|
||||
onChange={(e) => {
|
||||
const value = e.target.checked;
|
||||
setValue('questionGuide', value);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<ConfirmSaveModal />
|
||||
<ConfirmDelModal />
|
||||
{settingAppInfo && (
|
||||
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
||||
)}
|
||||
{editVariable && (
|
||||
<VariableEditModal
|
||||
defaultVariable={editVariable}
|
||||
@@ -573,9 +472,9 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
defaultData={getValues('chatModel')}
|
||||
/>
|
||||
)}
|
||||
{isOpenKbSelect && (
|
||||
{isOpenDatasetSelect && (
|
||||
<DatasetSelectModal
|
||||
isOpen={isOpenKbSelect}
|
||||
isOpen={isOpenDatasetSelect}
|
||||
activeDatasets={selectDatasets.map((item) => ({
|
||||
datasetId: item._id,
|
||||
vectorModel: item.vectorModel
|
||||
@@ -584,8 +483,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
onChange={replaceKbList}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isOpenKbParams && (
|
||||
{isOpenDatasetParams && (
|
||||
<DatasetParamsModal
|
||||
{...getValues('dataset')}
|
||||
onClose={onCloseKbParams}
|
||||
@@ -601,9 +499,156 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const ChatTest = ({ appId }: { appId: string }) => {
|
||||
function Settings({ appId }: { appId: string }) {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { parentRef, divRef, isSticky } = useSticky();
|
||||
const { appDetail } = useAppStore();
|
||||
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
||||
|
||||
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
|
||||
content: t('app.Confirm Del App Tip')
|
||||
});
|
||||
|
||||
/* 点击删除 */
|
||||
const { mutate: handleDelModel, isLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
if (!appDetail) return null;
|
||||
await delModelById(appDetail._id);
|
||||
return 'success';
|
||||
},
|
||||
onSuccess(res) {
|
||||
if (!res) return;
|
||||
toast({
|
||||
title: t('common.Delete Success'),
|
||||
status: 'success'
|
||||
});
|
||||
router.replace(`/app/list`);
|
||||
},
|
||||
errorToast: t('common.Delete Failed')
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={parentRef}
|
||||
h={'100%'}
|
||||
borderRight={'1.5px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
pt={[0, 4]}
|
||||
pb={10}
|
||||
overflow={'overlay'}
|
||||
>
|
||||
<Box px={4}>
|
||||
<Flex alignItems={'flex-end'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
<PermissionIconText permission={appDetail.permission} />
|
||||
</Box>
|
||||
<Box ml={1} color={'myGray.500'} fontSize={'sm'}>
|
||||
(AppId:{' '}
|
||||
<Box as={'span'} userSelect={'all'}>
|
||||
{appId}
|
||||
</Box>
|
||||
)
|
||||
</Box>
|
||||
</Flex>
|
||||
{/* basic info */}
|
||||
<Box
|
||||
border={theme.borders.base}
|
||||
borderRadius={'lg'}
|
||||
mt={2}
|
||||
px={5}
|
||||
py={4}
|
||||
bg={'myBlue.100'}
|
||||
position={'relative'}
|
||||
>
|
||||
<Flex alignItems={'center'} py={2}>
|
||||
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
|
||||
<Box ml={3} fontWeight={'bold'} fontSize={'lg'}>
|
||||
{appDetail.name}
|
||||
</Box>
|
||||
{appDetail.isOwner && (
|
||||
<IconButton
|
||||
className="delete"
|
||||
position={'absolute'}
|
||||
top={4}
|
||||
right={4}
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'delete'} w={'14px'} />}
|
||||
variant={'base'}
|
||||
borderRadius={'md'}
|
||||
aria-label={'delete'}
|
||||
_hover={{
|
||||
bg: 'myGray.100',
|
||||
color: 'red.600'
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
onClick={openConfirmDel(handleDelModel)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box
|
||||
flex={1}
|
||||
my={2}
|
||||
className={'textEllipsis3'}
|
||||
wordBreak={'break-all'}
|
||||
color={'myGray.600'}
|
||||
>
|
||||
{appDetail.intro || '快来给应用一个介绍~'}
|
||||
</Box>
|
||||
<Flex>
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
|
||||
onClick={() => router.push(`/chat?appId=${appId}`)}
|
||||
>
|
||||
对话
|
||||
</Button>
|
||||
<Button
|
||||
mx={3}
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'shareLight'} w={'16px'} />}
|
||||
onClick={() => {
|
||||
router.replace({
|
||||
query: {
|
||||
appId,
|
||||
currentTab: 'outLink'
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
外接
|
||||
</Button>
|
||||
{appDetail.isOwner && (
|
||||
<Button
|
||||
size={['sm', 'md']}
|
||||
variant={'base'}
|
||||
leftIcon={<MyIcon name={'settingLight'} w={'16px'} />}
|
||||
onClick={() => setSettingAppInfo(appDetail)}
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
{/* config form */}
|
||||
<ConfigForm divRef={divRef} isSticky={isSticky} />
|
||||
|
||||
<ConfirmDelModal />
|
||||
{settingAppInfo && (
|
||||
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ChatTest({ appId }: { appId: string }) {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { appDetail } = useAppStore();
|
||||
@@ -703,7 +748,7 @@ const ChatTest = ({ appId }: { appId: string }) => {
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const BasicEdit = ({ appId }: { appId: string }) => {
|
||||
const { isPc } = useSystemStore();
|
||||
|
@@ -99,7 +99,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
display={['none', 'flex']}
|
||||
flexDirection={'column'}
|
||||
p={4}
|
||||
w={'200px'}
|
||||
w={'180px'}
|
||||
borderRight={theme.borders.base}
|
||||
>
|
||||
<Flex mb={4} alignItems={'center'}>
|
||||
|
@@ -40,6 +40,7 @@ import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import PermissionIconText from '@/components/support/permission/IconText';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { DatasetItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import ParentPaths from '@/components/common/ParentPaths';
|
||||
|
||||
const CreateModal = dynamic(() => import('./component/CreateModal'), { ssr: false });
|
||||
const MoveModal = dynamic(() => import('./component/MoveModal'), { ssr: false });
|
||||
@@ -113,64 +114,34 @@ const Kb = () => {
|
||||
return Promise.all([loadDatasets(parentId), getDatasetPaths(parentId)]);
|
||||
});
|
||||
|
||||
const paths = useMemo(
|
||||
() => [
|
||||
{
|
||||
parentId: '',
|
||||
parentName: t('dataset.My Dataset')
|
||||
},
|
||||
...(data?.[1] || [])
|
||||
],
|
||||
[data, t]
|
||||
);
|
||||
const paths = data?.[1] || [];
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<Flex pt={3} px={5} alignItems={'center'}>
|
||||
{/* url path */}
|
||||
{!!parentId ? (
|
||||
<Flex flex={1}>
|
||||
{paths.map((item, i) => (
|
||||
<Flex key={item.parentId} mr={2} alignItems={'center'}>
|
||||
<Box
|
||||
fontSize={['sm', 'lg']}
|
||||
px={[0, 2]}
|
||||
py={1}
|
||||
borderRadius={'md'}
|
||||
{...(i === paths.length - 1
|
||||
? {
|
||||
cursor: 'default'
|
||||
}
|
||||
: {
|
||||
cursor: 'pointer',
|
||||
_hover: {
|
||||
bg: 'myGray.100'
|
||||
},
|
||||
onClick: () => {
|
||||
router.push({
|
||||
query: {
|
||||
parentId: item.parentId
|
||||
}
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
{item.parentName}
|
||||
</Box>
|
||||
{i !== paths.length - 1 && (
|
||||
<MyIcon name={'rightArrowLight'} color={'myGray.500'} w={['18px', '24px']} />
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex flex={1} alignItems={'center'}>
|
||||
<Image src={'/imgs/module/db.png'} alt={''} mr={2} h={'24px'} />
|
||||
<Box className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||
我的知识库
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
<ParentPaths
|
||||
paths={paths.map((path, i) => ({
|
||||
parentId: path.parentId,
|
||||
parentName: path.parentName
|
||||
}))}
|
||||
FirstPathDom={
|
||||
<Flex flex={1} alignItems={'center'}>
|
||||
<Image src={'/imgs/module/db.png'} alt={''} mr={2} h={'24px'} />
|
||||
<Box className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
|
||||
{t('dataset.My Dataset')}
|
||||
</Box>
|
||||
</Flex>
|
||||
}
|
||||
onClick={(e) => {
|
||||
router.push({
|
||||
query: {
|
||||
parentId: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{/* create icon */}
|
||||
{userInfo?.team?.canWrite && (
|
||||
<MyMenu
|
||||
offset={[-30, 10]}
|
||||
|
Reference in New Issue
Block a user