4.8.12 test (#3006)

* perf: oneapi error tip

* fix: qps limit condition error

* perf: Plan tip

* fix: permission modal ui

* perf: dataset slider ui

* perf: api key auth tmbId problem

* perf: dataset upload i18n

* fix: http json path check
This commit is contained in:
Archer
2024-10-28 22:47:45 +08:00
committed by GitHub
parent b712a821f8
commit e06d72e86e
29 changed files with 270 additions and 294 deletions

View File

@@ -126,29 +126,11 @@ const FolderSlideCard = ({
<MyDivider my={6} />
<Box>
<FormLabel>{t('common:support.permission.Permission')}</FormLabel>
{!isInheritPermission && (
<Box mt={2}>
<ResumeInherit onResume={() => resumeInheritPermission?.().then(refetchResource)} />
</Box>
)}
{managePer.permission.hasManagePer && !!defaultPer && (
<Box mt={5}>
<Box fontSize={'sm'} color={'myGray.500'}>
{t('common:permission.Default permission')}
</Box>
<DefaultPermissionList
mt="1"
per={defaultPer.value}
defaultPer={defaultPer.defaultValue}
isInheritPermission={isInheritPermission}
onChange={(v) => defaultPer.onChange(v)}
hasParent={hasParent}
/>
</Box>
)}
<Box mt={6}>
<CollaboratorContextProvider
{...managePer}
@@ -190,8 +172,8 @@ const FolderSlideCard = ({
<MemberListCard
mt={2}
tagStyle={{
type: 'borderSolid',
colorSchema: 'gray'
type: 'fill',
colorSchema: 'white'
}}
/>
</>

View File

@@ -114,6 +114,35 @@ const ChatInput = ({
renderAudioGraph,
stream
} = useSpeech({ appId, ...outLinkAuthData });
const onWhisperRecord = useCallback(() => {
const finishWhisperTranscription = (text: string) => {
if (!text) return;
if (whisperConfig?.autoSend) {
onSendMessage({
text,
files: fileList,
autoTTSResponse
});
replaceFiles([]);
} else {
resetInputVal({ text });
}
};
if (isSpeaking) {
return stopSpeak();
}
startSpeak(finishWhisperTranscription);
}, [
autoTTSResponse,
fileList,
isSpeaking,
onSendMessage,
replaceFiles,
resetInputVal,
startSpeak,
stopSpeak,
whisperConfig?.autoSend
]);
useEffect(() => {
if (!stream) {
return;
@@ -131,28 +160,6 @@ const ChatInput = ({
};
renderCurve();
}, [renderAudioGraph, stream]);
const finishWhisperTranscription = useCallback(
(text: string) => {
if (!text) return;
if (whisperConfig?.autoSend) {
onSendMessage({
text,
files: fileList,
autoTTSResponse
});
replaceFiles([]);
} else {
resetInputVal({ text });
}
},
[autoTTSResponse, fileList, onSendMessage, replaceFiles, resetInputVal, whisperConfig?.autoSend]
);
const onWhisperRecord = useCallback(() => {
if (isSpeaking) {
return stopSpeak();
}
startSpeak(finishWhisperTranscription);
}, [finishWhisperTranscription, isSpeaking, startSpeak, stopSpeak]);
const RenderTranslateLoading = useMemo(
() => (

View File

@@ -95,23 +95,19 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
iconSrc="modal/AddClb"
title={t('user:team.add_collaborator')}
minW="800px"
h={'100%'}
isCentered
isLoading={loadingMembersAndGroups}
>
<ModalBody>
<ModalBody flex={'1'}>
<Grid
border="1px solid"
borderColor="myGray.200"
borderRadius="0.5rem"
gridTemplateColumns="1fr 1fr"
h={'100%'}
>
<Flex
flexDirection="column"
borderRight="1px solid"
borderColor="myGray.200"
p="4"
minH="200px"
>
<Flex flexDirection="column" borderRight="1px solid" borderColor="myGray.200" p="4">
<SearchInput
placeholder={t('user:search_user')}
bgColor="myGray.50"

View File

@@ -325,6 +325,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
</Box>
);
};
const PlanUsage = () => {
const router = useRouter();
const { t } = useTranslation();

View File

@@ -527,15 +527,14 @@ const authHeaderRequest = async ({
teamId,
tmbId,
authType,
apikey,
canWrite: apiKeyCanWrite
apikey
} = await authCert({
req,
authToken: true,
authApiKey: true
});
const { app, canWrite } = await (async () => {
const { app } = await (async () => {
if (authType === AuthUserTypeEnum.apikey) {
if (!apiKeyAppId) {
return Promise.reject(
@@ -551,16 +550,14 @@ const authHeaderRequest = async ({
appId = String(app._id);
return {
app,
canWrite: apiKeyCanWrite
app
};
} else {
// token_auth
if (!appId) {
return Promise.reject('appId is empty');
}
const { app, permission } = await authApp({
const { app } = await authApp({
req,
authToken: true,
appId,
@@ -568,8 +565,7 @@ const authHeaderRequest = async ({
});
return {
app,
canWrite: permission.hasReadPer
app
};
}
})();
@@ -579,7 +575,12 @@ const authHeaderRequest = async ({
MongoChat.findOne({ appId, chatId }).lean()
]);
if (chat && (String(chat.teamId) !== teamId || String(chat.tmbId) !== tmbId)) {
if (
chat &&
(String(chat.teamId) !== teamId ||
// There's no need to distinguish who created it if it's apiKey auth
(authType === AuthUserTypeEnum.token && String(chat.tmbId) !== tmbId))
) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
@@ -591,7 +592,7 @@ const authHeaderRequest = async ({
responseDetail: true,
apikey,
authType,
canWrite
canWrite: true
};
};

View File

@@ -386,7 +386,7 @@ const Render = (props: Props) => {
return (
<>
<NextHead title={props.appName} desc={props.appIntro} icon={props.appAvatar} />
<NextHead title={props.appName || 'AI'} desc={props.appIntro} icon={props.appAvatar} />
{systemLoaded && (
<ChatContextProvider params={contextParams}>
<OutLink {...props} outLinkUid={contextParams.outLinkUid} />;

View File

@@ -15,11 +15,11 @@ function MemberManager({ managePer }: { managePer: MemberManagerInputPropsType }
return (
<>
<Flex alignItems="center" flexDirection="row" justifyContent="space-between" w="full">
<Box>
<FormLabel fontSize={'mini'}>{t('common:permission.Collaborator')}</FormLabel>
<Box color={'myGray.900'} fontSize={'mini'} fontWeight={'bold'}>
{t('common:permission.Collaborator')}
</Box>
<Flex gap={0.5}>
<Box p={1}>
<Flex gap={2}>
<Box>
<MyIcon
onClick={onOpenManageModal}
name="common/setting"
@@ -30,7 +30,7 @@ function MemberManager({ managePer }: { managePer: MemberManagerInputPropsType }
_hover={{ color: 'primary.500' }}
/>
</Box>
<Box p={1}>
<Box>
<MyIcon
cursor={'pointer'}
onClick={onOpenAddMember}

View File

@@ -182,7 +182,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: false,
showPromptInput: false,
charsPointsPrice: agentModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.Auto mode Estimated Price Tips', {
priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice
}),
uploadRate: 100
@@ -197,7 +197,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: true,
showPromptInput: false,
charsPointsPrice: vectorModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.Embedding Estimated Price Tips', {
priceTip: t('dataset:import.Embedding Estimated Price Tips', {
price: vectorModel.charsPointsPrice
}),
uploadRate: 150
@@ -212,8 +212,8 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: true,
showPromptInput: true,
charsPointsPrice: agentModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.QA Estimated Price Tips', {
price: agentModel?.charsPointsPrice
priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice
}),
uploadRate: 30
}

View File

@@ -78,7 +78,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
<Box h={'100%'} display={['block', 'flex']} fontSize={'sm'}>
<Box
flex={'1 0 0'}
minW={['auto', '540px']}
minW={['auto', '500px']}
maxW={'600px'}
h={['auto', '100%']}
overflow={'auto'}
@@ -86,11 +86,11 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
>
<Flex alignItems={'center'}>
<MyIcon name={'common/settingLight'} w={'20px'} />
<Box fontSize={'md'}>{t('common:core.dataset.import.Data process params')}</Box>
<Box fontSize={'md'}>{t('dataset:data_process_setting')}</Box>
</Flex>
<Box display={['block', 'flex']} mt={4} alignItems={'center'}>
<FormLabel flex={'0 0 100px'}>{t('common:core.dataset.import.Training mode')}</FormLabel>
<FormLabel flex={'0 0 100px'}>{t('dataset:training_mode')}</FormLabel>
<LeftRadio
list={trainingModeList.map(([key, value]) => ({
title: t(value.label as any),
@@ -107,8 +107,9 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
flexWrap={'wrap'}
/>
</Box>
<Box display={['block', 'flex']} mt={5}>
<FormLabel flex={'0 0 100px'}>{t('common:core.dataset.import.Process way')}</FormLabel>
<FormLabel flex={'0 0 100px'}>{t('dataset:data_process_params')}</FormLabel>
<LeftRadio
list={[
{
@@ -117,18 +118,16 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
value: ImportProcessWayEnum.auto
},
{
title: t('common:core.dataset.import.Custom process'),
desc: t('common:core.dataset.import.Custom process desc'),
title: t('dataset:custom_data_process_params'),
desc: t('dataset:custom_data_process_params_desc'),
value: ImportProcessWayEnum.custom,
children: way === ImportProcessWayEnum.custom && (
<Box mt={5}>
{showChunkInput && chunkSizeField && (
<Box>
<Flex alignItems={'center'}>
<Box>{t('common:core.dataset.import.Ideal chunk length')}</Box>
<MyTooltip
label={t('common:core.dataset.import.Ideal chunk length Tips')}
>
<Box>{t('dataset:ideal_chunk_length')}</Box>
<MyTooltip label={t('dataset:ideal_chunk_length_tips')}>
<MyIcon
name={'common/questionLight'}
ml={1}
@@ -269,15 +268,15 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
}}
></LeftRadio>
</Box>
<Box mt={5} pl={[0, '100px']} gap={3}>
{feConfigs?.show_pay && (
<MyTooltip label={priceTip}>
<MyTag colorSchema={'gray'} py={1.5} borderRadius={'md'} px={3} whiteSpace={'wrap'}>
{priceTip}
</MyTag>
</MyTooltip>
)}
</Box>
{feConfigs?.show_pay && (
<Box mt={5} pl={[0, '100px']} gap={3}>
<MyTag colorSchema={'gray'} py={1.5} borderRadius={'md'} px={3} whiteSpace={'wrap'}>
{priceTip}
</MyTag>
</Box>
)}
<Flex mt={5} gap={3} justifyContent={'flex-end'}>
<Button
onClick={() => {

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Box, Flex, IconButton } from '@chakra-ui/react';
import { Box, Flex, Grid, IconButton } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
@@ -19,68 +19,69 @@ const Preview = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => {
const [previewChunkSource, setPreviewChunkSource] = useState<ImportSourceItemType>();
return (
<Box h={'100%'} display={['block', 'flex']} flexDirection={'column'}>
<Box h={'100%'} w={'100%'} display={['block', 'flex']} flexDirection={'column'}>
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'20px'} />
<Box fontSize={'md'}>{t('common:core.dataset.import.Sources list')}</Box>
</Flex>
<Box mt={3} flex={'1 0 0'} width={'100%'} overflow={'auto'}>
{sources.map((source) => (
<Flex
key={source.id}
bg={'white'}
p={4}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'borderColor.low'}
boxShadow={'2'}
mb={3}
alignItems={'center'}
>
<MyIcon name={source.icon as any} w={'16px'} />
<Box mx={1} flex={'1 0 0'} w={0} className="textEllipsis">
{source.sourceName}
</Box>
{showPreviewChunks && (
<Box fontSize={'xs'} color={'myGray.600'}>
<MyMenu
Button={
<IconButton
icon={<MyIcon name={'common/viewLight'} w={'14px'} p={2} />}
aria-label={''}
size={'sm'}
variant={'whitePrimary'}
/>
}
menuList={[
{
children: [
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview raw text')}
</Flex>
),
onClick: () => setPreviewRawTextSource(source)
},
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/splitLight'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview chunks')}
</Flex>
),
onClick: () => setPreviewChunkSource(source)
}
]
}
]}
/>
<Box mt={3} flex={'1 0 0'} width={'100%'} overflowY={'auto'}>
<Grid w={'100%'} gap={3} gridTemplateColumns={['1fr', '1fr', '1fr', '1fr', '1fr 1fr']}>
{sources.map((source) => (
<Flex
key={source.id}
bg={'white'}
p={4}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'borderColor.low'}
boxShadow={'2'}
alignItems={'center'}
>
<MyIcon name={source.icon as any} w={['1rem', '1.25rem']} />
<Box mx={1} flex={'1 0 0'} wordBreak={'break-all'} fontSize={'sm'}>
{source.sourceName}
</Box>
)}
</Flex>
))}
{showPreviewChunks && (
<Box fontSize={'xs'} color={'myGray.600'}>
<MyMenu
Button={
<IconButton
icon={<MyIcon name={'common/viewLight'} w={'14px'} p={2} />}
aria-label={''}
size={'sm'}
variant={'whitePrimary'}
/>
}
menuList={[
{
children: [
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview raw text')}
</Flex>
),
onClick: () => setPreviewRawTextSource(source)
},
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/splitLight'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview chunks')}
</Flex>
),
onClick: () => setPreviewChunkSource(source)
}
]
}
]}
/>
</Box>
)}
</Flex>
))}
</Grid>
</Box>
{!!previewRawTextSource && (
<PreviewRawText

View File

@@ -33,8 +33,6 @@ import { EditResourceInfoFormType } from '@/components/common/Modal/EditResource
const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal'));
const Info = ({ datasetId }: { datasetId: string }) => {
const [openBaseConfig, setOpenBaseConfig] = useState(true);
const [openPermissionConfig, setOpenPermissionConfig] = useState(true);
const { t } = useTranslation();
const { datasetDetail, loadDatasetDetail, updateDataset, rebuildingCount, trainingCount } =
useContextSelector(DatasetPageContext, (v) => v);
@@ -175,23 +173,13 @@ const Info = ({ datasetId }: { datasetId: string }) => {
<MyDivider my={4} h={'2px'} maxW={'500px'} />
<Box overflow={'hidden'} h={openBaseConfig ? 'auto' : '24px'}>
<Box overflow={'hidden'}>
<Flex justify={'space-between'} alignItems={'center'} fontSize={'mini'} h={'24px'}>
<Box fontWeight={'500'} color={'myGray.900'} userSelect={'none'}>
{t('common:common.base_config')}
</Box>
<MyIcon
w={'16px'}
_hover={{ color: 'primary.500', cursor: 'pointer' }}
color={'myGray.500'}
name={openBaseConfig ? 'core/chat/chevronUp' : 'core/chat/chevronDown'}
onClick={(e) => {
e.preventDefault();
setOpenBaseConfig(!openBaseConfig);
}}
/>
</Flex>
<Flex mt={3} w={'100%'} flexDir={'column'} userSelect={'none'}>
<Flex mt={3} w={'100%'} flexDir={'column'}>
<FormLabel fontSize={'mini'} fontWeight={'500'}>
{t('common:core.dataset.Dataset ID')}
</FormLabel>
@@ -202,7 +190,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
<FormLabel fontSize={'mini'} fontWeight={'500'}>
{t('common:core.ai.model.Vector Model')}
</FormLabel>
<Box pt={2} flex={[1, '0 0 320px']}>
<Box pt={2}>
<AIModelSelector
w={'100%'}
value={vectorModel.model}
@@ -231,12 +219,10 @@ const Info = ({ datasetId }: { datasetId: string }) => {
</Box>
<Flex mt={2} w={'100%'} alignItems={'center'}>
<FormLabel flex={['0 0 90px', '0 0 160px']} fontSize={'mini'} w={0} fontWeight={'500'}>
<FormLabel flex={1} fontSize={'mini'} w={0} fontWeight={'500'}>
{t('common:core.Max Token')}
</FormLabel>
<Box flex={[1, '0 0 320px']} fontSize={'mini'}>
{vectorModel.maxToken}
</Box>
<Box fontSize={'mini'}>{vectorModel.maxToken}</Box>
</Flex>
<Box pt={5}>
@@ -286,54 +272,33 @@ const Info = ({ datasetId }: { datasetId: string }) => {
{datasetDetail.permission.hasManagePer && (
<>
<MyDivider my={4} h={'2px'} maxW={'500px'} />
<Box overflow={'hidden'} h={openPermissionConfig ? 'auto' : '24px'}>
<Flex justify={'space-between'} alignItems={'center'} fontSize={'mini'} h={'24px'}>
<Box fontWeight={'500'} color={'myGray.900'} userSelect={'none'}>
{t('common:permission.Permission config')}
</Box>
<MyIcon
w={'16px'}
_hover={{ color: 'primary.500', cursor: 'pointer' }}
color={'myGray.500'}
name={openPermissionConfig ? 'core/chat/chevronUp' : 'core/chat/chevronDown'}
onClick={() => setOpenPermissionConfig(!openPermissionConfig)}
/>
</Flex>
<Box mt={3}>
<FormLabel fontWeight={'500'} fontSize={'mini'} pb={3} userSelect={'none'}>
{t('common:permission.Default permission')}
</FormLabel>
</Box>
<Box py={4}>
<MemberManager
managePer={{
mode: 'all',
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,
datasetId
}),
onDelOneCollaborator: async ({ groupId, tmbId }) => {
if (tmbId) {
return deleteDatasetCollaborators({
datasetId,
tmbId
});
} else if (groupId) {
return deleteDatasetCollaborators({
datasetId,
groupId
});
}
<Box>
<MemberManager
managePer={{
mode: 'all',
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,
datasetId
}),
onDelOneCollaborator: async ({ groupId, tmbId }) => {
if (tmbId) {
return deleteDatasetCollaborators({
datasetId,
tmbId
});
} else if (groupId) {
return deleteDatasetCollaborators({
datasetId,
groupId
});
}
}}
/>
</Box>
}
}}
/>
</Box>
</>
)}

View File

@@ -13,7 +13,6 @@ import { useTranslation } from 'next-i18next';
import React, { useCallback, useState } from 'react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
import { useForm } from 'react-hook-form';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
@@ -272,31 +271,15 @@ const ExtraPlan = () => {
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
{t('common:support.wallet.buy_resource')}
</Flex>
{/* <Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']} color={'myGray.600'}>
{t('common:support.wallet.subscription.Month amount')}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
<NumberInput size={'sm'} flex={1} step={1} min={1} max={12} position={'relative'}>
<NumberInputField
pr={'30px'}
{...registerExtraPoints('month', {
required: true,
min: 1,
max: 12,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
{t('common:common.month')}
</Box>
<Box>1</Box>
<Box color={'myGray.600'}>{t('common:common.month')}</Box>
</Flex>
</Flex> */}
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']} color={'myGray.600'}>
{t('common:support.wallet.subscription.Update extra ai points')}

View File

@@ -84,21 +84,35 @@ export const useSpeech = (props?: OutLinkChatAuthProps & { appId?: string }) =>
mediaRecorder.current.onstop = async () => {
if (!cancelWhisperSignal.current) {
const formData = new FormData();
let options = {};
const { options, filename } = (() => {
if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
return {
options: { mimeType: 'video/webm; codecs=vp9' },
filename: 'recording.mp3'
};
}
if (MediaRecorder.isTypeSupported('video/webm')) {
return {
options: { type: 'video/webm' },
filename: 'recording.mp3'
};
}
if (MediaRecorder.isTypeSupported('video/mp4')) {
return {
options: { mimeType: 'video/mp4', videoBitsPerSecond: 100000 },
filename: 'recording.mp4'
};
}
return {
options: { type: 'video/webm' },
filename: 'recording.mp3'
};
})();
if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
options = { mimeType: 'video/webm; codecs=vp9' };
} else if (MediaRecorder.isTypeSupported('video/webm')) {
options = { type: 'video/webm' };
} else if (MediaRecorder.isTypeSupported('video/mp4')) {
options = { mimeType: 'video/mp4', videoBitsPerSecond: 100000 };
} else {
console.error('no suitable mimetype found for this device');
}
const blob = new Blob(chunks, options);
const duration = Math.round((Date.now() - startTimestamp.current) / 1000);
formData.append('file', blob, 'recording.mp3');
console.log(options, filename, '=-=-');
formData.append('file', blob, filename);
formData.append(
'data',
JSON.stringify({