perf: app edit
This commit is contained in:
@@ -40,6 +40,10 @@ import { useRequest } from '@/hooks/useRequest';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||
import { streamFetch } from '@/api/fetch';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { AppSchema } from '@/types/mongoSchema';
|
||||
import { delModelById } from '@/api/app';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import MySelect from '@/components/Select';
|
||||
@@ -56,15 +60,17 @@ import { addVariable } from '../VariableEditModal';
|
||||
import { KBSelectModal, KbParamsModal } from '../KBSelectModal';
|
||||
|
||||
const VariableEditModal = dynamic(() => import('../VariableEditModal'));
|
||||
const InfoModal = dynamic(() => import('../InfoModal'));
|
||||
|
||||
const Settings = ({ appId }: { appId: string }) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { appDetail, updateAppDetail, loadKbList, myKbList } = useUserStore();
|
||||
const { isPc } = useGlobalStore();
|
||||
|
||||
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
||||
|
||||
useQuery(['initkb', appId], () => loadKbList());
|
||||
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
@@ -117,6 +123,24 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
[myKbList, kbList]
|
||||
);
|
||||
|
||||
/* 点击删除 */
|
||||
const { mutate: handleDelModel, isLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
if (!appDetail) return null;
|
||||
await delModelById(appDetail._id);
|
||||
return 'success';
|
||||
},
|
||||
onSuccess(res) {
|
||||
if (!res) return;
|
||||
toast({
|
||||
title: '删除成功',
|
||||
status: 'success'
|
||||
});
|
||||
router.replace(`/app/list`);
|
||||
},
|
||||
errorToast: '删除失败'
|
||||
});
|
||||
|
||||
const appModule2Form = useCallback(() => {
|
||||
const formVal = appModules2Form(appDetail.modules);
|
||||
reset(formVal);
|
||||
@@ -139,6 +163,8 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
appModule2Form();
|
||||
}, [appModule2Form]);
|
||||
|
||||
useQuery(['initkb', appId], () => loadKbList());
|
||||
|
||||
const BoxStyles: BoxProps = {
|
||||
bg: 'myWhite.200',
|
||||
px: 4,
|
||||
@@ -163,15 +189,95 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
display={['block', 'flex']}
|
||||
flexDirection={'column'}
|
||||
h={'100%'}
|
||||
borderRight={'1.5px solid'}
|
||||
borderColor={'myGray.200'}
|
||||
pt={4}
|
||||
pl={4}
|
||||
p={4}
|
||||
pt={[0, 4]}
|
||||
overflow={'overlay'}
|
||||
>
|
||||
<Flex pr={4} justifyContent={'space-between'}>
|
||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||
基础信息
|
||||
</Box>
|
||||
{/* 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>
|
||||
<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={openConfirm(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={'chatLight'} 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: 'share'
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
分享
|
||||
</Button>
|
||||
<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>
|
||||
@@ -181,221 +287,219 @@ const Settings = ({ appId }: { appId: string }) => {
|
||||
<Button
|
||||
isLoading={isSaving}
|
||||
fontSize={'sm'}
|
||||
size={['sm', 'md']}
|
||||
onClick={openConfirm(handleSubmit((data) => onSubmitSave(data)))}
|
||||
>
|
||||
{isPc ? '保存并预览' : '保存'}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box flex={'1 0 0'} my={4} pr={4} overflowY={'auto'}>
|
||||
{/* variable */}
|
||||
<Box {...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>
|
||||
<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>
|
||||
|
||||
{/* variable */}
|
||||
<Box mt={2} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
||||
<Box ml={2} flex={1}>
|
||||
变量
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<Box ml={2}>AI 配置</Box>
|
||||
<Flex {...BoxBtnStyles} onClick={() => setEditVariable(addVariable())}>
|
||||
+ 新增
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>对话模型</Box>
|
||||
<MySelect
|
||||
width={['100%', '300px']}
|
||||
value={getValues('chatModel.model')}
|
||||
list={chatModelSelectList}
|
||||
onchange={(val: any) => {
|
||||
setValue('chatModel.model', val);
|
||||
const maxToken =
|
||||
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
||||
?.contextMaxToken || 4000;
|
||||
const token = maxToken / 2;
|
||||
setValue('chatModel.maxToken', token);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} my={10}>
|
||||
<Box {...LabelStyles}>温度</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '严谨', value: 0 },
|
||||
{ label: '发散', value: 10 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
max={10}
|
||||
value={getValues('chatModel.temperature')}
|
||||
onChange={(e) => {
|
||||
setValue('chatModel.temperature', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={12} mb={10}>
|
||||
<Box {...LabelStyles}>回复上限</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${tokenLimit}`, value: tokenLimit }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={getValues('chatModel.maxToken')}
|
||||
onChange={(val) => {
|
||||
setValue('chatModel.maxToken', val);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
提示词
|
||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Textarea
|
||||
rows={5}
|
||||
placeholder={ChatModelSystemTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.systemPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
限定词
|
||||
<MyTooltip label={ChatModelLimitTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Textarea
|
||||
rows={5}
|
||||
placeholder={ChatModelLimitTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.limitPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* kb */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<Box ml={2}>知识库</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||
<SmallAddIcon />
|
||||
选择
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||
参数
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
|
||||
空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'}
|
||||
</Flex>
|
||||
<Grid templateColumns={['1fr', 'repeat(2,1fr)']} my={2} gridGap={[2, 4]}>
|
||||
{selectedKbList.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
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}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
||||
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* 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')}
|
||||
/>
|
||||
</Flex>
|
||||
<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>
|
||||
|
||||
{/* model */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||
<Box ml={2}>AI 配置</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>对话模型</Box>
|
||||
<MySelect
|
||||
width={['100%', '300px']}
|
||||
value={getValues('chatModel.model')}
|
||||
list={chatModelSelectList}
|
||||
onchange={(val: any) => {
|
||||
setValue('chatModel.model', val);
|
||||
const maxToken =
|
||||
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
||||
?.contextMaxToken || 4000;
|
||||
const token = maxToken / 2;
|
||||
setValue('chatModel.maxToken', token);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} my={10}>
|
||||
<Box {...LabelStyles}>温度</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '严谨', value: 0 },
|
||||
{ label: '发散', value: 10 }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={0}
|
||||
max={10}
|
||||
value={getValues('chatModel.temperature')}
|
||||
onChange={(e) => {
|
||||
setValue('chatModel.temperature', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={12} mb={10}>
|
||||
<Box {...LabelStyles}>回复上限</Box>
|
||||
<Box flex={1} ml={'10px'}>
|
||||
<MySlider
|
||||
markList={[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${tokenLimit}`, value: tokenLimit }
|
||||
]}
|
||||
width={'95%'}
|
||||
min={100}
|
||||
max={tokenLimit}
|
||||
step={50}
|
||||
value={getValues('chatModel.maxToken')}
|
||||
onChange={(val) => {
|
||||
setValue('chatModel.maxToken', val);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex mt={10} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
提示词
|
||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Textarea
|
||||
rows={5}
|
||||
placeholder={ChatModelSystemTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.systemPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'flex-start'}>
|
||||
<Box {...LabelStyles}>
|
||||
限定词
|
||||
<MyTooltip label={ChatModelLimitTip} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
<Textarea
|
||||
rows={5}
|
||||
placeholder={ChatModelLimitTip}
|
||||
borderColor={'myGray.100'}
|
||||
{...register('chatModel.limitPrompt')}
|
||||
></Textarea>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* kb */}
|
||||
<Box mt={5} {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||
<Box ml={2}>知识库</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||
<SmallAddIcon />
|
||||
选择
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||
参数
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
|
||||
空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'}
|
||||
</Flex>
|
||||
<Grid templateColumns={['1fr', 'repeat(2,1fr)']} my={2} gridGap={[2, 4]}>
|
||||
{selectedKbList.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
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}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
||||
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* 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>
|
||||
|
||||
<ConfirmChild />
|
||||
{settingAppInfo && (
|
||||
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
||||
)}
|
||||
{editVariable && (
|
||||
<VariableEditModal
|
||||
defaultVariable={editVariable}
|
||||
|
||||
Reference in New Issue
Block a user