4.8.8 test fix (#2143)

* perf: transcriptions api

* perf: variable picker tip

* perf: variable picker tip

* perf: chat select app

* feat: router to app detail

* perf: variable avoid space

* perf: variable picker

* perf: doc2x icon and params

* perf: sandbox support countToken

* feat: sandbox support delay and countToken
This commit is contained in:
Archer
2024-07-24 16:02:53 +08:00
committed by GitHub
parent a478621730
commit 45b8d7e8de
49 changed files with 521 additions and 527 deletions

View File

@@ -0,0 +1,16 @@
import { Box, HStack, StackProps } from '@chakra-ui/react';
import React from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
const VariableTip = (props: StackProps) => {
const { t } = useTranslation();
return (
<HStack fontSize={'xs'} spacing={1} {...props}>
<MyIcon name={'common/info'} w={'0.9rem'} transform={'translateY(1px)'} />
<Box>{t('common:textarea_variable_picker_tip')}</Box>
</HStack>
);
};
export default VariableTip;

View File

@@ -127,6 +127,7 @@ const DatasetParamsModal = ({
>
<ModalBody flex={'auto'} overflow={'auto'}>
<LightRowTabs<SearchSettingTabEnum>
width={'100%'}
mb={3}
list={[
{

View File

@@ -299,6 +299,8 @@ const VariableEdit = ({
</Button>
<Button
onClick={handleSubmitEdit(({ variable }) => {
variable.key = variable.key.trim();
// check select
if (variable.type === VariableInputEnum.select) {
const enums = variable.enums.filter((item) => item.value);

View File

@@ -157,6 +157,7 @@ export const ResponseBox = React.memo(function ResponseBox({
{!hideTabs && (
<Box>
<LightRowTabs
w={'100%'}
list={list}
value={currentTab}
inlineStyles={{ pt: 0 }}

View File

@@ -3,15 +3,14 @@ import { jsonRes } from '@fastgpt/service/common/response';
import { getUploadModel } from '@fastgpt/service/common/file/multer';
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
import fs from 'fs';
import { getAIApi } from '@fastgpt/service/core/ai/config';
import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
import { authChatCert } from '@/service/support/permission/auth/chat';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { NextAPI } from '@/service/middleware/entry';
import { aiTranscriptions } from '@fastgpt/service/core/ai/audio/transcriptions';
const upload = getUploadModel({
maxSize: 2
maxSize: 20
});
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
@@ -45,6 +44,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
// auth role
const { teamId, tmbId } = await authChatCert({ req, authToken: true });
// auth app
// const app = await MongoApp.findById(appId, 'modules').lean();
// if (!app) {
@@ -54,11 +54,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
// throw new Error('Whisper is not open in the app');
// }
const ai = getAIApi();
const result = await ai.audio.transcriptions.create({
file: fs.createReadStream(file.path),
model: global.whisperModel.model
const result = await aiTranscriptions({
model: global.whisperModel.model,
fileStream: fs.createReadStream(file.path)
});
pushWhisperUsage({

View File

@@ -34,6 +34,7 @@ import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
@@ -57,7 +58,8 @@ const LabelStyles: BoxProps = {
w: ['60px', '100px'],
whiteSpace: 'nowrap',
flexShrink: 0,
fontSize: 'xs'
fontSize: 'sm',
color: 'myGray.900'
};
const EditForm = ({
@@ -162,10 +164,13 @@ const EditForm = ({
</Box>
</Flex>
<Box mt={3}>
<HStack {...LabelStyles}>
<Box mt={4}>
<HStack {...LabelStyles} w={'100%'}>
<Box>{t('common:core.ai.Prompt')}</Box>
<QuestionTip label={t('common:core.app.tip.chatNodeSystemPromptTip')} />
<Box flex={1} />
<VariableTip color={'myGray.500'} />
</HStack>
<Box mt={1}>
<PromptEditor

View File

@@ -309,6 +309,7 @@ export function RenderHttpProps({
></QuestionTip>
</Flex>
<LightRowTabs<TabEnum>
width={'100%'}
list={[
{ label: <RenderPropsItem text="Params" num={paramsLength} />, value: TabEnum.params },
...(!['GET', 'DELETE'].includes(requestMethods)

View File

@@ -1,7 +1,7 @@
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d';
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, Flex } from '@chakra-ui/react';
import { Box, Flex, HStack } from '@chakra-ui/react';
import NodeInputSelect from '@fastgpt/web/components/core/workflow/NodeInputSelect';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
@@ -10,6 +10,10 @@ import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';
type Props = {
nodeId: string;
@@ -68,6 +72,14 @@ const InputLabel = ({ nodeId, input }: Props) => {
/>
</Box>
)}
{/* Variable picker tip */}
{input.renderTypeList[input.selectedTypeIndex ?? 0] === FlowNodeInputTypeEnum.textarea && (
<>
<Box flex={1} />
<VariableTip transform={'translateY(2px)'} />
</>
)}
</Flex>
);
}, [

View File

@@ -27,50 +27,45 @@ const ChatHeader = ({
chatData,
history,
showHistory,
onRoute2AppDetail,
apps
apps,
onRouteToAppDetail
}: {
history: ChatItemType[];
showHistory?: boolean;
onRoute2AppDetail?: () => void;
apps?: AppListItemType[];
chatData: InitChatResponse;
apps?: AppListItemType[];
onRouteToAppDetail?: () => void;
}) => {
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
const { isPc } = useSystem();
return (
<>
{isPc && isPlugin ? null : (
<Flex
alignItems={'center'}
px={[3, 5]}
minH={['46px', '60px']}
borderBottom={'sm'}
color={'myGray.900'}
fontSize={'sm'}
>
{isPc ? (
<PcHeader
title={chatData.title}
chatModels={chatData.app.chatModels}
history={history}
/>
) : (
<MobileHeader
apps={apps}
appId={chatData.appId}
go2AppDetail={onRoute2AppDetail}
name={chatData.app.name}
avatar={chatData.app.avatar}
showHistory={showHistory}
/>
)}
{/* control */}
{!isPlugin && <ToolMenu history={history} />}
</Flex>
return isPc && isPlugin ? null : (
<Flex
alignItems={'center'}
px={[3, 5]}
minH={['46px', '60px']}
borderBottom={'sm'}
color={'myGray.900'}
fontSize={'sm'}
>
{isPc ? (
<>
<PcHeader title={chatData.title} chatModels={chatData.app.chatModels} history={history} />
<Box flex={1} />
</>
) : (
<MobileHeader
apps={apps}
appId={chatData.appId}
name={chatData.app.name}
avatar={chatData.app.avatar}
showHistory={showHistory}
/>
)}
</>
{/* control */}
{!isPlugin && <ToolMenu history={history} onRouteToAppDetail={onRouteToAppDetail} />}
</Flex>
);
};
@@ -88,7 +83,6 @@ const MobileDrawer = ({
app = 'app'
}
const { t } = useTranslation();
const { isPc } = useSystem();
const router = useRouter();
const isTeamChat = router.pathname === '/chat/team';
const [currentTab, setCurrentTab] = useState<TabEnum>(TabEnum.recently);
@@ -103,129 +97,120 @@ const MobileDrawer = ({
);
}, []);
const { onChangeAppId } = useContextSelector(ChatContext, (v) => v);
const onclickApp = (id: string) => {
onChangeAppId(id);
onCloseDrawer();
};
return (
<>
<Box
position={'absolute'}
top={'45px'}
w={'100vw'}
h={'calc(100% - 45px)'}
background={'rgba(0, 0, 0, 0.2)'}
left={0}
zIndex={5}
onClick={() => {
onCloseDrawer();
}}
>
{/* menu */}
<Box
position={'absolute'}
top={'45px'}
w={'100vw'}
h={'calc(100% - 45px)'}
background={'rgba(0, 0, 0, 0.2)'}
left={0}
zIndex={5}
onClick={() => {
onCloseDrawer();
}}
px={[2, 5]}
padding={2}
onClick={(e) => e.stopPropagation()}
background={'white'}
position={'relative'}
>
{/* menu */}
<Box
w={'100vw'}
px={[2, 5]}
padding={2}
onClick={(e) => e.stopPropagation()}
background={'white'}
position={'relative'}
>
{!isPc && appId && (
<LightRowTabs<TabEnum>
flex={'1 0 0'}
width={isTeamChat ? '30%' : '60%'}
mr={10}
inlineStyles={{
px: 1
}}
list={[
...(isTeamChat
? [{ label: t('common:all_apps'), value: TabEnum.recently }]
: [
{ label: t('common:core.chat.Recent use'), value: TabEnum.recently },
{ label: t('common:all_apps'), value: TabEnum.app }
])
]}
value={currentTab}
onChange={setCurrentTab}
/>
)}
</Box>
<Box
width={'100vw'}
height={'auto'}
minH={'10vh'}
maxH={'60vh'}
overflow={'auto'}
background={'white'}
zIndex={3}
onClick={(e) => e.stopPropagation()}
borderRadius={'0 0 10px 10px'}
position={'relative'}
padding={3}
pt={0}
pb={4}
>
{/* history */}
{currentTab === TabEnum.recently && (
<>
{Array.isArray(apps) &&
apps.map((item) => (
<Flex justify={'center'} key={item._id}>
<Flex
py={2.5}
px={2}
width={'100%'}
borderRadius={'md'}
alignItems={'center'}
{...(item._id === appId
? {
backgroundColor: 'primary.50 !important',
color: 'primary.600'
}
: {
onClick: () => onChangeAppId(item._id)
})}
>
<Avatar src={item.avatar} w={'24px'} />
<Box ml={2} className={'textEllipsis'}>
{item.name}
</Box>
</Flex>
</Flex>
))}
</>
)}
{currentTab === TabEnum.app && !isPc && (
<>
<SelectOneResource
value={appId}
onSelect={(id) => {
if (!id) return;
onChangeAppId(id);
}}
server={getAppList}
/>
</>
)}
</Box>
<LightRowTabs<TabEnum>
gap={3}
inlineStyles={{
px: 2
}}
list={[
...(isTeamChat
? [{ label: t('common:all_apps'), value: TabEnum.recently }]
: [
{ label: t('common:core.chat.Recent use'), value: TabEnum.recently },
{ label: t('common:all_apps'), value: TabEnum.app }
])
]}
value={currentTab}
onChange={setCurrentTab}
/>
</Box>
</>
<Box
width={'100%'}
minH={'10vh'}
background={'white'}
onClick={(e) => e.stopPropagation()}
borderRadius={'0 0 10px 10px'}
position={'relative'}
py={3}
pt={0}
pb={4}
h={'65vh'}
>
{/* history */}
{currentTab === TabEnum.recently && (
<Box px={3} overflow={'auto'} h={'100%'}>
{Array.isArray(apps) &&
apps.map((item) => (
<Flex justify={'center'} key={item._id}>
<Flex
py={2.5}
px={2}
width={'100%'}
borderRadius={'md'}
alignItems={'center'}
{...(item._id === appId
? {
backgroundColor: 'primary.50 !important',
color: 'primary.600'
}
: {
onClick: () => onclickApp(item._id)
})}
>
<Avatar src={item.avatar} w={'24px'} />
<Box ml={2} className={'textEllipsis'}>
{item.name}
</Box>
</Flex>
</Flex>
))}
</Box>
)}
{currentTab === TabEnum.app && (
<SelectOneResource
value={appId}
onSelect={(id) => {
if (!id) return;
onclickApp(id);
}}
server={getAppList}
/>
)}
</Box>
</Box>
);
};
const MobileHeader = ({
showHistory,
go2AppDetail,
name,
avatar,
appId,
apps
}: {
showHistory?: boolean;
go2AppDetail?: () => void;
avatar: string;
name: string;
apps?: AppListItemType[];
appId: string;
}) => {
const { isPc } = useSystem();
const router = useRouter();
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
const { isOpen: isOpenDrawer, onToggle: toggleDrawer, onClose: onCloseDrawer } = useDisclosure();
@@ -237,22 +222,21 @@ const MobileHeader = ({
<MyIcon name={'menu'} w={'20px'} h={'20px'} color={'myGray.900'} onClick={onOpenSlider} />
)}
<Flex px={3} alignItems={'center'} flex={'1 0 0'} w={0} justifyContent={'center'}>
<Avatar src={avatar} w={'16px'} />
<Box ml={1} className="textEllipsis" onClick={go2AppDetail}>
{name}
</Box>
{isShareChat ? null : (
<MyIcon
_active={{ transform: 'scale(0.9)' }}
name={'core/chat/chevronSelector'}
w={'20px'}
h={'20px'}
color={isOpenDrawer ? 'primary.600' : 'myGray.900'}
onClick={toggleDrawer}
/>
)}
<Flex alignItems={'center'} onClick={toggleDrawer}>
<Avatar src={avatar} w={'1rem'} />
<Box ml={1} className="textEllipsis">
{name}
</Box>
{isShareChat ? null : (
<MyIcon
name={'core/chat/chevronSelector'}
w={'1.25rem'}
color={isOpenDrawer ? 'primary.600' : 'myGray.900'}
/>
)}
</Flex>
</Flex>
{!isPc && isOpenDrawer && !isShareChat && (
{isOpenDrawer && !isShareChat && (
<MobileDrawer apps={apps} appId={appId} onCloseDrawer={onCloseDrawer} />
)}
</>
@@ -292,7 +276,6 @@ const PcHeader = ({
</MyTag>
</MyTooltip>
)}
<Box flex={1} />
</>
);
};

View File

@@ -27,7 +27,6 @@ const ChatHistorySlider = ({
appId,
appName,
appAvatar,
apps = [],
confirmClearText,
onDelHistory,
onClearHistory,
@@ -37,7 +36,6 @@ const ChatHistorySlider = ({
appId?: string;
appName: string;
appAvatar: string;
apps?: AppListItemType[];
confirmClearText: string;
onDelHistory: (e: { chatId: string }) => void;
onClearHistory: () => void;
@@ -46,10 +44,9 @@ const ChatHistorySlider = ({
}) => {
const theme = useTheme();
const router = useRouter();
const isTeamChat = router.pathname === '/chat/team';
const isUserChatPage = router.pathname === '/chat';
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystem();
const { userInfo } = useUserStore();
@@ -103,7 +100,7 @@ const ChatHistorySlider = ({
whiteSpace={'nowrap'}
>
{isPc && (
<MyTooltip label={canRouteToDetail ? appT('app_detail') : ''} offset={[0, 0]}>
<MyTooltip label={canRouteToDetail ? t('app:app_detail') : ''} offset={[0, 0]}>
<Flex
pt={5}
pb={2}
@@ -136,7 +133,7 @@ const ChatHistorySlider = ({
justify={['space-between', '']}
alignItems={'center'}
>
{!isPc && appId && (
{!isPc && (
<Flex height={'100%'} align={'center'} justify={'center'}>
<MyIcon ml={2} name="core/chat/sideLine" />
<Box ml={2} fontWeight={'bold'}>
@@ -147,8 +144,9 @@ const ChatHistorySlider = ({
<Button
variant={'whitePrimary'}
flex={[appId ? '0 0 auto' : 1, 1]}
flex={['0 0 auto', 1]}
h={'100%'}
px={6}
color={'primary.600'}
borderRadius={'xl'}
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
@@ -158,7 +156,7 @@ const ChatHistorySlider = ({
{t('common:core.chat.New Chat')}
</Button>
{/* Clear */}
{isPc && (
{isPc && histories.length > 0 && (
<IconButton
ml={3}
h={'100%'}
@@ -288,7 +286,7 @@ const ChatHistorySlider = ({
</Box>
{/* exec */}
{!isPc && appId && !isTeamChat && (
{!isPc && isUserChatPage && (
<Flex
mt={2}
borderTop={theme.borders.base}

View File

@@ -7,7 +7,13 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
const ToolMenu = ({
history,
onRouteToAppDetail
}: {
history: ChatItemType[];
onRouteToAppDetail?: () => void;
}) => {
const { t } = useTranslation();
const { onExportChat } = useChatBox();
const router = useRouter();
@@ -57,7 +63,20 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
// onClick: () => onExportChat({ type: 'pdf', history })
// }
]
}
},
...(onRouteToAppDetail
? [
{
children: [
{
icon: 'core/app/aiLight',
label: t('app:app_detail'),
onClick: onRouteToAppDetail
}
]
}
]
: [])
]}
/>
) : (

View File

@@ -195,7 +195,6 @@ const Chat = ({
);
})(
<ChatHistorySlider
apps={myApps}
confirmClearText={t('common:core.chat.Confirm to clear history')}
appId={appId}
appName={chatData.app.name}
@@ -229,8 +228,8 @@ const Chat = ({
apps={myApps}
chatData={chatData}
history={chatRecords}
onRoute2AppDetail={() => router.push(`/app/detail?appId=${appId}`)}
showHistory
onRouteToAppDetail={() => router.push(`/app/detail?appId=${appId}`)}
/>
{/* chat box */}
@@ -341,7 +340,7 @@ export async function getServerSideProps(context: any) {
props: {
appId: context?.query?.appId || '',
chatId: context?.query?.chatId || '',
...(await serviceSideProps(context, ['file']))
...(await serviceSideProps(context, ['file', 'app']))
}
};
}

View File

@@ -292,7 +292,7 @@ const OutLink = ({ appName, appIntro, appAvatar }: Props) => {
{showHead === '1' ? (
<ChatHeader
chatData={chatData}
history={chatData.history}
history={chatRecords}
showHistory={showHistory === '1'}
/>
) : null}
@@ -396,7 +396,7 @@ export async function getServerSideProps(context: any) {
appIntro: app?.appId?.intro ?? 'intro',
shareId: shareId ?? '',
authToken: authToken ?? '',
...(await serviceSideProps(context, ['file']))
...(await serviceSideProps(context, ['file', 'app']))
}
};
}

View File

@@ -199,7 +199,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
})(
<ChatHistorySlider
appId={appId}
apps={myApps}
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
confirmClearText={t('common:core.chat.Confirm to clear history')}
@@ -230,7 +229,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
flexDirection={'column'}
>
{/* header */}
<ChatHeader apps={myApps} chatData={chatData} history={chatData.history} showHistory />
<ChatHeader apps={myApps} chatData={chatData} history={chatRecords} showHistory />
{/* chat box */}
<Box flex={1}>
{chatData.app.type === AppTypeEnum.plugin ? (
@@ -340,7 +339,7 @@ export async function getServerSideProps(context: any) {
chatId: context?.query?.chatId || '',
teamId: context?.query?.teamId || '',
teamToken: context?.query?.teamToken || '',
...(await serviceSideProps(context, ['file']))
...(await serviceSideProps(context, ['file', 'app']))
}
};
}

View File

@@ -128,7 +128,7 @@ const Login = () => {
export async function getServerSideProps(context: any) {
return {
props: { ...(await serviceSideProps(context)) }
props: { ...(await serviceSideProps(context, ['app'])) }
};
}

View File

@@ -439,7 +439,7 @@ export const simpleBotTemplates: TemplateType = [
label: 'core.ai.Prompt',
description: 'core.app.tip.chatNodeSystemPromptTip',
placeholder: 'core.app.tip.chatNodeSystemPromptTip',
value: '请直接将我的问题翻译成{{language}},不需要回答问题。'
value: '请直接将我的问题翻译成{{$VARIABLE_NODE_ID.language$}},不需要回答问题。'
},
{
key: 'history',