This commit is contained in:
archer 2025-05-30 12:54:25 +08:00
parent 3b0f0a8108
commit e74ab643fe
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
16 changed files with 310 additions and 395 deletions

View File

@ -50,6 +50,7 @@ export type SelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
showBorder?: boolean; showBorder?: boolean;
}[]; }[];
isLoading?: boolean; isLoading?: boolean;
showAvatar?: boolean;
onChange?: (val: T) => any | Promise<any>; onChange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData']; ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
customOnOpen?: () => void; customOnOpen?: () => void;
@ -79,6 +80,7 @@ const MySelect = <T = any,>(
list = [], list = [],
onChange, onChange,
isLoading = false, isLoading = false,
showAvatar = true,
ScrollData, ScrollData,
customOnOpen, customOnOpen,
customOnClose, customOnClose,
@ -255,7 +257,7 @@ const MySelect = <T = any,>(
/> />
) : ( ) : (
<> <>
{selectItem?.icon && ( {selectItem?.icon && showAvatar && (
<Avatar <Avatar
mr={2} mr={2}
src={selectItem.icon as any} src={selectItem.icon as any}

View File

@ -19,6 +19,7 @@
"image_upload": "Image upload", "image_upload": "Image upload",
"no_gate_available": "No portal available", "no_gate_available": "No portal available",
"no_gate_to_delete": "There is no gate to delete", "no_gate_to_delete": "There is no gate to delete",
"quick_app": "Quick Application",
"slogan": "slogan", "slogan": "slogan",
"status": "state", "status": "state",
"suggestion_ratio_1_1": "Suggested ratio 1:1", "suggestion_ratio_1_1": "Suggested ratio 1:1",

View File

@ -20,6 +20,7 @@
"image_upload": "图片上传", "image_upload": "图片上传",
"no_gate_available": "没有可用门户", "no_gate_available": "没有可用门户",
"no_gate_to_delete": "没有可以删除的门户了", "no_gate_to_delete": "没有可以删除的门户了",
"quick_app": "快捷应用",
"slogan": "标语", "slogan": "标语",
"status": "状态", "status": "状态",
"suggestion_ratio_1_1": "建议比例 1:1", "suggestion_ratio_1_1": "建议比例 1:1",

View File

@ -19,6 +19,7 @@
"image_upload": "圖片上傳", "image_upload": "圖片上傳",
"no_gate_available": "沒有可用門戶", "no_gate_available": "沒有可用門戶",
"no_gate_to_delete": "沒有可以刪除的門戶了", "no_gate_to_delete": "沒有可以刪除的門戶了",
"quick_app": "快捷應用",
"slogan": "標語", "slogan": "標語",
"status": "狀態", "status": "狀態",
"suggestion_ratio_1_1": "建議比例 1:1", "suggestion_ratio_1_1": "建議比例 1:1",

View File

@ -110,7 +110,14 @@ const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
</Box> </Box>
); );
}; };
const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props }: Props) => { const MultipleRowSelector = ({
list,
onChange,
disableTip,
placeholder,
showAvatar = true,
...props
}: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } = const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore(); useSystemStore();
@ -193,17 +200,20 @@ const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props
return ( return (
<HStack spacing={1}> <HStack spacing={1}>
<Avatar {showAvatar && (
borderRadius={'0'} <Avatar
mr={2} borderRadius={'0'}
src={modelData?.avatar} mr={2}
fallbackSrc={HUGGING_FACE_ICON} src={modelData?.avatar}
w={avatarSize} fallbackSrc={HUGGING_FACE_ICON}
/> w={avatarSize}
/>
)}
<Box>{modelData?.name}</Box> <Box>{modelData?.name}</Box>
</HStack> </HStack>
); );
}, [modelList, props.value, t, avatarSize]); }, [modelList, props.value, t, showAvatar, avatarSize]);
return ( return (
<Box <Box

View File

@ -1,5 +1,5 @@
import React, { useRef, useCallback, useMemo, useState, useEffect, useContext } from 'react'; import React, { useRef, useCallback, useMemo, useState, useEffect, useContext } from 'react';
import { Box, Flex, Textarea, IconButton, useBreakpointValue, Button } from '@chakra-ui/react'; import { Box, Flex, Textarea, IconButton, useBreakpointValue } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getWebDefaultLLMModel } from '@/web/common/system/utils'; import { getWebDefaultLLMModel } from '@/web/common/system/utils';
@ -23,7 +23,8 @@ import dynamic from 'next/dynamic';
import { AppContext } from '@/pageComponents/app/detail/context'; import { AppContext } from '@/pageComponents/app/detail/context';
import { AppFormContext } from '@/pages/chat/gate/index'; import { AppFormContext } from '@/pages/chat/gate/index';
import Icon from '@fastgpt/web/components/common/Icon'; import Icon from '@fastgpt/web/components/common/Icon';
import GateSelect from '@fastgpt/web/components/common/MySelect/GateSelect'; import AIModelSelector from '@/components/Select/AIModelSelector';
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
const GateToolSelect = dynamic( const GateToolSelect = dynamic(
() => import('@/pageComponents/app/detail/Gate/components/GateToolSelect'), () => import('@/pageComponents/app/detail/Gate/components/GateToolSelect'),
@ -46,8 +47,8 @@ type Props = {
resetInputVal: (val: ChatBoxInputType) => void; resetInputVal: (val: ChatBoxInputType) => void;
chatForm: UseFormReturn<ChatBoxInputFormType>; chatForm: UseFormReturn<ChatBoxInputFormType>;
placeholder?: string; placeholder?: string;
selectedToolIds?: string[]; selectedTools?: FlowNodeTemplateType[];
onSelectedToolIdsChange?: (toolIds: string[]) => void; onSelectTools?: (toolIds: FlowNodeTemplateType[]) => void;
}; };
const GateChatInput = ({ const GateChatInput = ({
@ -57,8 +58,8 @@ const GateChatInput = ({
resetInputVal, resetInputVal,
chatForm, chatForm,
placeholder, placeholder,
selectedToolIds: externalSelectedToolIds, selectedTools: externalSelectedToolIds,
onSelectedToolIdsChange onSelectTools
}: Props) => { }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
@ -78,10 +79,9 @@ const GateChatInput = ({
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting); const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
const fileSelectConfig = useContextSelector(ChatBoxContext, (v) => v.fileSelectConfig); const fileSelectConfig = useContextSelector(ChatBoxContext, (v) => v.fileSelectConfig);
// 如果有外部传入的工具选择,使用外部的;否则使用内部状态 // eslint-disable-next-line react-hooks/exhaustive-deps
const [internalSelectedToolIds, setInternalSelectedToolIds] = useState<string[]>([]); const selectedTools = externalSelectedToolIds ?? [];
const selectedToolIds = externalSelectedToolIds ?? internalSelectedToolIds; const setSelectedToolIds = onSelectTools!;
const setSelectedToolIds = onSelectedToolIdsChange ?? setInternalSelectedToolIds;
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const { llmModelList } = useSystemStore(); const { llmModelList } = useSystemStore();
@ -93,10 +93,7 @@ const GateChatInput = ({
const [selectedModel, setSelectedModel] = useState(defaultModel); const [selectedModel, setSelectedModel] = useState(defaultModel);
const showModelSelector = useMemo(() => { const showModelSelector = useMemo(() => {
return ( return router.pathname === '/chat/gate';
router.pathname.startsWith('/chat/gate') &&
!router.pathname.includes('/chat/gate/application')
);
}, [router.pathname]); }, [router.pathname]);
// 是否显示工具选择器 // 是否显示工具选择器
@ -188,7 +185,7 @@ const GateChatInput = ({
text: textareaValue.trim(), text: textareaValue.trim(),
files: fileList, files: fileList,
gateModel: showModelSelector ? selectedModel : undefined, gateModel: showModelSelector ? selectedModel : undefined,
selectedTool: selectedToolIds.length > 0 ? selectedToolIds.join(',') : null // 将工具ID数组转换为逗号分隔的字符串 selectedTool: selectedTools.length > 0 ? selectedTools.join(',') : null // 将工具ID数组转换为逗号分隔的字符串
}); });
replaceFiles([]); replaceFiles([]);
}, },
@ -200,7 +197,7 @@ const GateChatInput = ({
replaceFiles, replaceFiles,
showModelSelector, showModelSelector,
selectedModel, selectedModel,
selectedToolIds selectedTools
] ]
); );
@ -214,8 +211,9 @@ const GateChatInput = ({
boxShadow="0px 5px 16px -4px rgba(19, 51, 107, 0.08)" boxShadow="0px 5px 16px -4px rgba(19, 51, 107, 0.08)"
borderRadius="20px" borderRadius="20px"
position="relative" position="relative"
p={4} px={4}
pb="56px" pb={'62px'}
pt={fileList.length > 0 ? 0 : 4}
overflow="hidden" overflow="hidden"
transition="all 0.2s ease" transition="all 0.2s ease"
_hover={{ _hover={{
@ -335,24 +333,18 @@ const GateChatInput = ({
> >
<Flex align="center" gap={2} overflow="hidden" maxW="65%" flexShrink={1} flexWrap="nowrap"> <Flex align="center" gap={2} overflow="hidden" maxW="65%" flexShrink={1} flexWrap="nowrap">
{showModelSelector && ( {showModelSelector && (
<GateSelect <AIModelSelector
value={selectedModel}
list={modelList} list={modelList}
value={selectedModel}
showAvatar={false}
onChange={setSelectedModel} onChange={setSelectedModel}
minW="128px" bg={'myGray.50'}
maxW="180px" borderRadius={'lg'}
w="auto"
bg="#F9F9F9"
border="0.5px solid #E0E0E0"
borderRadius="10px"
color="#485264"
h="36px"
fontSize="14px"
/> />
)} )}
{showTools && ( {showTools && (
<GateToolSelect <GateToolSelect
selectedToolIds={selectedToolIds} selectedTools={selectedTools}
onToolsChange={setSelectedToolIds} onToolsChange={setSelectedToolIds}
buttonSize={buttonSize} buttonSize={buttonSize}
/> />

View File

@ -14,7 +14,7 @@ import type {
} from '@fastgpt/global/core/chat/type.d'; } from '@fastgpt/global/core/chat/type.d';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { Box, Checkbox, Flex, Text } from '@chakra-ui/react'; import { Box, Checkbox, Flex, HStack, Text } from '@chakra-ui/react';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus'; import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt'; import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
@ -74,6 +74,9 @@ import { useRouter } from 'next/router';
import { getTeamGateConfig, getTeamGateConfigCopyRight } from '@/web/support/user/team/gate/api'; import { getTeamGateConfig, getTeamGateConfigCopyRight } from '@/web/support/user/team/gate/api';
import type { getGateConfigCopyRightResponse } from '@fastgpt/global/support/user/team/gate/api'; import type { getGateConfigCopyRightResponse } from '@fastgpt/global/support/user/team/gate/api';
import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type'; import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type';
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
import type { AppListItemType } from '@fastgpt/global/core/app/type';
import Avatar from '@fastgpt/web/components/common/Avatar';
const FeedbackModal = dynamic(() => import('./components/FeedbackModal')); const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal')); const ReadFeedbackModal = dynamic(() => import('./components/ReadFeedbackModal'));
@ -96,8 +99,9 @@ type Props = OutLinkChatAuthProps &
showVoiceIcon?: boolean; showVoiceIcon?: boolean;
showEmptyIntro?: boolean; showEmptyIntro?: boolean;
active?: boolean; // can use active?: boolean; // can use
selectedToolIds?: string[]; selectedTools?: FlowNodeTemplateType[];
onSelectedToolIdsChange?: (toolIds: string[]) => void; onSelectTools?: (toolIds: FlowNodeTemplateType[]) => void;
recommendApps?: AppListItemType[];
onStartChat?: (e: StartChatFnProps) => Promise< onStartChat?: (e: StartChatFnProps) => Promise<
StreamResponseType & { StreamResponseType & {
@ -115,8 +119,9 @@ const ChatBox = ({
active = true, active = true,
onStartChat, onStartChat,
chatType, chatType,
selectedToolIds, selectedTools,
onSelectedToolIdsChange onSelectTools,
recommendApps = []
}: Props) => { }: Props) => {
const ScrollContainerRef = useRef<HTMLDivElement>(null); const ScrollContainerRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation(); const { t } = useTranslation();
@ -421,7 +426,7 @@ const ChatBox = ({
const router = useRouter(); const router = useRouter();
const inGateRoute = useMemo(() => { const inGateRoute = useMemo(() => {
return router.pathname.startsWith('/chat/gate'); return router.pathname === '/chat/gate';
}, [router.pathname]); }, [router.pathname]);
/** /**
* user confirm send prompt * user confirm send prompt
@ -1124,11 +1129,7 @@ const ChatBox = ({
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const showWelcome = useMemo(() => { const showWelcome = useMemo(() => {
return ( return router.pathname === '/chat/gate' && chatRecords.length === 0;
router.pathname.startsWith('/chat/gate') &&
!router.pathname.includes('/chat/gate/application') &&
chatRecords.length === 0
);
}, [router.pathname, chatRecords.length]); }, [router.pathname, chatRecords.length]);
return ( return (
@ -1169,6 +1170,29 @@ const ChatBox = ({
{/* message input */} {/* message input */}
{onStartChat && chatStarted && active && !isInteractive && ( {onStartChat && chatStarted && active && !isInteractive && (
<Box w={{ base: 'calc(100% - 48px)', md: '700px' }} maxH="132px" h="100%" px={0}> <Box w={{ base: 'calc(100% - 48px)', md: '700px' }} maxH="132px" h="100%" px={0}>
<HStack mb={3}>
{recommendApps.map((item) => (
<HStack
gap={1}
key={item._id}
border={'base'}
px={2}
py={1}
borderRadius={'sm'}
cursor={'pointer'}
_hover={{
bg: 'primary.50',
borderColor: 'primary.400'
}}
onClick={() => {
router.push(`/chat/gate/application?appId=${item._id}`);
}}
>
<Avatar src={item.avatar} w="1rem" h="1rem" borderRadius={'sm'} />
<Box fontSize={'sm'}>{item.name}</Box>
</HStack>
))}
</HStack>
<GateChatInput <GateChatInput
onSendMessage={sendPrompt} onSendMessage={sendPrompt}
onStop={() => chatController.current?.abort('stop')} onStop={() => chatController.current?.abort('stop')}
@ -1176,8 +1200,8 @@ const ChatBox = ({
resetInputVal={resetInputVal} resetInputVal={resetInputVal}
chatForm={chatForm} chatForm={chatForm}
placeholder={gateConfig?.placeholderText || '你可以问我任何问题'} placeholder={gateConfig?.placeholderText || '你可以问我任何问题'}
selectedToolIds={selectedToolIds} selectedTools={selectedTools}
onSelectedToolIdsChange={onSelectedToolIdsChange} onSelectTools={onSelectTools}
/> />
</Box> </Box>
)} )}
@ -1205,7 +1229,7 @@ const ChatBox = ({
{RenderRecords} {RenderRecords}
{/* 移动端下的输入框和版权信息容器 */} {/* 移动端下的输入框和版权信息容器 */}
<Flex direction="column" w="100%" mb={{ base: '12px', sm: 0 }} gap="12px"> <Flex direction="column" w="100%" mb={{ base: '12px', sm: 0 }}>
{/* message input */} {/* message input */}
{onStartChat && chatStarted && active && !isInteractive && ( {onStartChat && chatStarted && active && !isInteractive && (
<Box <Box
@ -1225,8 +1249,8 @@ const ChatBox = ({
resetInputVal={resetInputVal} resetInputVal={resetInputVal}
chatForm={chatForm} chatForm={chatForm}
placeholder={gateConfig?.placeholderText || t('common:gate.placeholder')} placeholder={gateConfig?.placeholderText || t('common:gate.placeholder')}
selectedToolIds={selectedToolIds} selectedTools={selectedTools}
onSelectedToolIdsChange={onSelectedToolIdsChange} onSelectTools={onSelectTools}
/> />
)} )}
{!inGateRoute && ( {!inGateRoute && (

View File

@ -7,7 +7,6 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import ShareGateModal from './ShareModol'; import ShareGateModal from './ShareModol';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getMyAppsGate, postCreateApp, putAppById } from '@/web/core/app/api'; import { getMyAppsGate, postCreateApp, putAppById } from '@/web/core/app/api';
import { useUserStore } from '@/web/support/user/useUserStore';
import { emptyTemplates } from '@/web/core/app/templates'; import { emptyTemplates } from '@/web/core/app/templates';
import { saveGateConfig } from './HomeTable'; import { saveGateConfig } from './HomeTable';
import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type'; import type { GateSchemaType } from '@fastgpt/global/support/user/team/gate/type';
@ -32,7 +31,10 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
const { runAsync: saveHomeConfig, loading: savingHome } = useRequest2( const { runAsync: saveHomeConfig, loading: savingHome } = useRequest2(
async () => { async () => {
if (!!gateConfig) { if (!!gateConfig) {
await saveGateConfig(gateConfig); await saveGateConfig({
...gateConfig,
status: true
});
toast({ toast({
title: t('common:save_success'), title: t('common:save_success'),
status: 'success' status: 'success'
@ -89,7 +91,6 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
const gateApp = apps.find((app) => app.type === AppTypeEnum.gate); const gateApp = apps.find((app) => app.type === AppTypeEnum.gate);
const currentTeamAvatar = copyRightConfig?.logo; const currentTeamAvatar = copyRightConfig?.logo;
const currentSlogan = gateConfig?.slogan; const currentSlogan = gateConfig?.slogan;
console.log('gateApp', gateApp, currentTeamAvatar, currentSlogan, nodes, edges);
if (gateApp) { if (gateApp) {
if ( if (
gateApp.avatar !== currentTeamAvatar || gateApp.avatar !== currentTeamAvatar ||
@ -104,25 +105,17 @@ const ConfigButtons = ({ tab, appForm, gateConfig, copyRightConfig }: Props) =>
nodes, nodes,
edges edges
}); });
toast({
title: t('common:update_success'),
status: 'success'
});
} }
} else { } else {
await postCreateApp({ await postCreateApp({
avatar: gateConfig?.logo, avatar: gateConfig?.logo,
name: gateConfig?.name, name: 'App',
intro: gateConfig?.slogan, intro: gateConfig?.slogan,
type: AppTypeEnum.gate, type: AppTypeEnum.gate,
modules: emptyTemplates[AppTypeEnum.gate].nodes, modules: emptyTemplates[AppTypeEnum.gate].nodes,
edges: emptyTemplates[AppTypeEnum.gate].edges, edges: emptyTemplates[AppTypeEnum.gate].edges,
chatConfig: emptyTemplates[AppTypeEnum.gate].chatConfig chatConfig: emptyTemplates[AppTypeEnum.gate].chatConfig
}); });
toast({
title: t('common:create_success'),
status: 'success'
});
} }
} catch (error) { } catch (error) {
toast({ toast({

View File

@ -22,7 +22,6 @@ import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { useSimpleAppSnapshots } from '@/pageComponents/app/detail/Gate/useSnapshots'; import { useSimpleAppSnapshots } from '@/pageComponents/app/detail/Gate/useSnapshots';
import { Dropdown } from 'react-day-picker';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import AddQuickAppModal from './AddQuickAppModal'; import AddQuickAppModal from './AddQuickAppModal';
import { listQuickApps } from '@/web/support/user/team/gate/quickApp'; import { listQuickApps } from '@/web/support/user/team/gate/quickApp';
@ -43,7 +42,6 @@ type Props = {
tools: string[]; tools: string[];
slogan: string; slogan: string;
placeholderText: string; placeholderText: string;
onStatusChange?: (status: boolean) => void;
onSloganChange?: (slogan: string) => void; onSloganChange?: (slogan: string) => void;
onPlaceholderChange?: (text: string) => void; onPlaceholderChange?: (text: string) => void;
onToolsChange?: (tools: string[]) => void; onToolsChange?: (tools: string[]) => void;
@ -54,7 +52,6 @@ const HomeTable = ({
appDetail, appDetail,
slogan, slogan,
placeholderText, placeholderText,
onStatusChange,
onSloganChange, onSloganChange,
onPlaceholderChange, onPlaceholderChange,
onAppFormChange onAppFormChange
@ -165,12 +162,6 @@ const HomeTable = ({
letterSpacing: '0.25px' letterSpacing: '0.25px'
}; };
// 响应式工具布局
const handleStatusChange = (val: string) => {
onStatusChange?.(val === 'enabled');
};
const handleSloganChange = (val: string) => { const handleSloganChange = (val: string) => {
onSloganChange?.(val); onSloganChange?.(val);
}; };
@ -287,75 +278,6 @@ const HomeTable = ({
pb={6} pb={6}
pt={{ base: 4, md: 6 }} pt={{ base: 4, md: 6 }}
> >
{/* 状态选择 */}
<FormControl display="flex" flexDirection="column" gap={spacing.sm} w="full">
<FormLabel
fontWeight={formStyles.fontWeight}
fontSize={formStyles.fontSize}
lineHeight={formStyles.lineHeight}
letterSpacing={formStyles.letterSpacing}
color="myGray.700"
mb="0"
>
{t('account_gate:status')}
</FormLabel>
<RadioGroup value={status ? 'enabled' : 'disabled'} onChange={handleStatusChange}>
<Stack direction={{ base: 'column', sm: 'row' }} spacing={spacing.md}>
<Flex
alignItems="center"
p={`${spacing.sm} ${spacing.lg} ${spacing.sm} ${spacing.md}`}
borderWidth="1px"
borderColor={status ? 'primary.500' : 'myGray.200'}
borderRadius="7px"
bg={status ? 'blue.50' : 'white'}
transition="all 0.2s ease-in-out"
_hover={{
bg: status ? 'blue.100' : 'myGray.50',
borderColor: status ? 'primary.600' : 'myGray.300',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.05)',
transform: 'translateY(-1px)'
}}
>
<Radio value="enabled" colorScheme="blue">
<Text
fontSize={formStyles.fontSize}
lineHeight={formStyles.lineHeight}
fontWeight={formStyles.fontWeight}
letterSpacing={formStyles.letterSpacing}
>
{t('account_gate:enabled')}
</Text>
</Radio>
</Flex>
<Flex
alignItems="center"
p={`${spacing.sm} ${spacing.lg} ${spacing.sm} ${spacing.md}`}
borderWidth="1px"
borderColor={!status ? 'primary.500' : 'myGray.200'}
borderRadius="7px"
bg={!status ? 'blue.50' : 'white'}
transition="all 0.2s ease-in-out"
_hover={{
bg: !status ? 'blue.100' : 'myGray.50',
borderColor: !status ? 'primary.600' : 'myGray.300',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.05)',
transform: 'translateY(-1px)'
}}
>
<Radio value="disabled" colorScheme="blue">
<Text
fontSize={formStyles.fontSize}
lineHeight={formStyles.lineHeight}
fontWeight={formStyles.fontWeight}
letterSpacing={formStyles.letterSpacing}
>
{t('account_gate:disabled')}
</Text>
</Radio>
</Flex>
</Stack>
</RadioGroup>
</FormControl>
{/* 快捷应用 */} {/* 快捷应用 */}
<FormControl <FormControl
display={'flex'} display={'flex'}
@ -365,7 +287,7 @@ const HomeTable = ({
gap={'8px'} gap={'8px'}
> >
{/* 标题行 */} {/* 标题行 */}
<Flex alignItems={'center'} gap={'4px'}> <Flex alignItems={'center'} gap={'1'}>
<Text <Text
color={'var(--Gray-Modern-600, #485264)'} color={'var(--Gray-Modern-600, #485264)'}
fontFamily={'PingFang SC'} fontFamily={'PingFang SC'}

View File

@ -1,11 +1,10 @@
import { Box, Button, Flex, Grid, useDisclosure, Text } from '@chakra-ui/react'; import { Box, Button, Flex, Grid, useDisclosure, Text } from '@chakra-ui/react';
import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react'; import React, { useState, useCallback } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { SmallAddIcon } from '@chakra-ui/icons'; import { SmallAddIcon } from '@chakra-ui/icons';
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { theme } from '@fastgpt/web/styles/theme';
import { import {
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
FlowNodeTypeEnum FlowNodeTypeEnum
@ -113,9 +112,8 @@ const ToolSelect = ({
<> <>
{/* 标题区域 */} {/* 标题区域 */}
<Flex alignItems="center" justifyContent="space-between" width="100%"> <Flex alignItems="center" justifyContent="space-between" width="100%">
<Flex alignItems="center" gap={spacing.xs}> <Flex alignItems="center" gap={1}>
<Text <Text
ml={2}
fontWeight={formStyles.fontWeight} fontWeight={formStyles.fontWeight}
fontSize={formStyles.fontSize} fontSize={formStyles.fontSize}
lineHeight={formStyles.lineHeight} lineHeight={formStyles.lineHeight}
@ -124,7 +122,7 @@ const ToolSelect = ({
> >
{t('common:core.app.Tool call')} {t('common:core.app.Tool call')}
</Text> </Text>
<QuestionTip ml={1} label={t('app:plugin_dispatch_tip')} /> <QuestionTip label={t('app:plugin_dispatch_tip')} />
</Flex> </Flex>
{/* 已有工具时显示新增按钮 */} {/* 已有工具时显示新增按钮 */}

View File

@ -1,7 +1,6 @@
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex, HStack } from '@chakra-ui/react';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { useSafeState } from 'ahooks';
import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { useChatGate } from '../useChatGate'; import { useChatGate } from '../useChatGate';
@ -11,6 +10,9 @@ import { useChatStore } from '@/web/core/chat/context/useChatStore';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { cardStyles } from '../constants'; import { cardStyles } from '../constants';
import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { getQuickApps, listQuickApps } from '@/web/support/user/team/gate/quickApp';
import Avatar from '@fastgpt/web/components/common/Avatar';
type Props = { type Props = {
appForm: AppSimpleEditFormType; appForm: AppSimpleEditFormType;
@ -18,31 +20,17 @@ type Props = {
appDetail: AppDetailType; // 添加 appDetail prop appDetail: AppDetailType; // 添加 appDetail prop
}; };
const ChatGate = ({ appForm, setRenderEdit, appDetail }: Props) => { const ChatGate = ({ appForm, setRenderEdit, appDetail }: Props) => {
console.log('appDetai', appDetail);
console.log('appform', appForm);
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData); const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData); const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
// 添加 selectedToolIds 状态管理
const [selectedToolIds, setSelectedToolIds] = useState<string[]>([]);
const [workflowData] = useSafeState({
nodes: appDetail.modules || [],
edges: appDetail.edges || []
});
useEffect(() => { useEffect(() => {
setRenderEdit(!datasetCiteData); setRenderEdit(!datasetCiteData);
}, [datasetCiteData, setRenderEdit]); }, [datasetCiteData, setRenderEdit]);
const { ChatContainer, restartChat, loading } = useChatGate({ const { ChatContainer } = useChatGate({
...workflowData, appForm,
chatConfig: appForm.chatConfig,
isReady: true, isReady: true,
appDetail, appDetail
selectedToolIds, // 传递 selectedToolIds
onSelectedToolIdsChange: setSelectedToolIds // 传递更新函数
}); });
return ( return (

View File

@ -12,25 +12,31 @@ import {
ModalHeader, ModalHeader,
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
useDisclosure useDisclosure,
HStack
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { getTeamGateConfig } from '@/web/support/user/team/gate/api'; import { getTeamGateConfig } from '@/web/support/user/team/gate/api';
import { getSystemPlugTemplates, getTeamPlugTemplates } from '@/web/core/app/api/plugin'; import {
import type { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node.d'; getPreviewPluginNode,
getSystemPlugTemplates,
getTeamPlugTemplates
} from '@/web/core/app/api/plugin';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
import MyBox from '@fastgpt/web/components/common/MyBox';
type GateToolSelectProps = { type GateToolSelectProps = {
selectedToolIds: string[]; selectedTools: FlowNodeTemplateType[];
onToolsChange: (toolIds: string[]) => void; onToolsChange: (tools: FlowNodeTemplateType[]) => void;
buttonSize?: string; buttonSize?: string;
}; };
const GateToolSelect = ({ const GateToolSelect = ({
selectedToolIds, selectedTools,
onToolsChange, onToolsChange,
buttonSize = 'md' buttonSize = 'md'
}: GateToolSelectProps) => { }: GateToolSelectProps) => {
@ -77,60 +83,83 @@ const GateToolSelect = ({
}, [gateConfig?.tools, allAvailableTools]); }, [gateConfig?.tools, allAvailableTools]);
// 处理单个工具的选择/取消选择 // 处理单个工具的选择/取消选择
const handleToolSelect = useCallback( const { runAsync: handleToolSelect, loading: loadingToolSelect } = useRequest2(
(toolId: string, checked: boolean) => { async (toolId: string, checked: boolean) => {
const newSelectedIds = checked const tool = allAvailableTools.find((item) => item.id === toolId);
? [...selectedToolIds, toolId] if (!tool) return;
: selectedToolIds.filter((id) => id !== toolId);
onToolsChange(newSelectedIds); if (checked) {
const res = await getPreviewPluginNode({ appId: tool.id });
onToolsChange([...selectedTools, res]);
} else {
onToolsChange(selectedTools.filter((item) => item.pluginId !== toolId));
}
}, },
[selectedToolIds, onToolsChange] {
refreshDeps: [allAvailableTools, selectedTools, onToolsChange]
}
); );
const selectedCount = selectedToolIds.length; const selectedCount = selectedTools.length;
const loading = loadingGateConfig || loadingSystemPlugins || loadingTeamPlugins; const loading = loadingGateConfig || loadingSystemPlugins || loadingTeamPlugins;
// 调试信息 return availableTools.length > 0 ? (
console.log('GateToolSelect Debug:', {
isOpen,
loading,
availableTools: availableTools.length,
gateConfigTools: gateConfig?.tools?.length || 0,
systemPlugins: systemPlugins.length,
teamPlugins: teamPlugins.length,
allAvailableTools: allAvailableTools.length
});
return (
<> <>
<Button {availableTools.length > 1 ? (
leftIcon={ <Button
<MyIcon name={'support/gate/chat/toolkitLine'} w={'18px'} h={'18px'} color="blue.500" /> leftIcon={
} <MyIcon name={'support/gate/chat/toolkitLine'} w={'18px'} h={'18px'} color="blue.500" />
size={buttonSize} }
display="flex" size={buttonSize}
padding="8px 12px" display="flex"
justifyContent="center" padding="8px 12px"
alignItems="center" justifyContent="center"
gap="4px" alignItems="center"
iconSpacing="4px" gap="4px"
borderRadius="9999px" iconSpacing="4px"
border="0.5px solid var(--Royal-Blue-200, #C5D7FF)" borderRadius="9999px"
background="var(--light-fastgpt-primary-container-low, #F0F4FF)" border="0.5px solid var(--Royal-Blue-200, #C5D7FF)"
color="blue.500" background="var(--light-fastgpt-primary-container-low, #F0F4FF)"
fontWeight="500" color="blue.500"
onClick={() => { fontWeight="500"
console.log('Button clicked, opening modal'); onClick={onOpen}
onOpen(); flexShrink={0}
}} _hover={{
flexShrink={0} background: 'var(--light-fastgpt-primary-container-low, #E6EDFF)'
_hover={{ }}
background: 'var(--light-fastgpt-primary-container-low, #E6EDFF)' >
}} <Box display={{ base: 'none', md: 'block' }}>{t('common:tool_select')}:&nbsp;</Box>
> {selectedCount}
<Box display={{ base: 'none', md: 'block' }}>{t('common:tool_select')}:&nbsp;</Box> </Button>
{selectedCount} ) : (
</Button> <MyBox
isLoading={loadingToolSelect}
display={'flex'}
alignItems={'center'}
gap={2}
h={'40px'}
borderRadius={'lg'}
border={'base'}
px={3}
cursor={'pointer'}
userSelect={'none'}
_hover={{
borderColor: 'primary.300'
}}
{...(selectedTools.length > 0 && {
borderColor: 'primary.400 !important',
bg: 'primary.50'
})}
onClick={() => {
handleToolSelect(availableTools[0].id, selectedTools.length === 0);
}}
>
<Avatar src={availableTools[0].avatar} w="1.2rem" h="1.2rem" borderRadius={'sm'} />
<Text display={['none', 'none', 'block']} fontSize="md" fontWeight="medium">
{availableTools[0].name}
</Text>
</MyBox>
)}
<Modal isOpen={isOpen} onClose={onClose} size="md"> <Modal isOpen={isOpen} onClose={onClose} size="md">
<ModalOverlay /> <ModalOverlay />
@ -152,76 +181,83 @@ const GateToolSelect = ({
<ModalCloseButton /> <ModalCloseButton />
<ModalBody pb={6}> <ModalBody pb={6}>
{loading ? ( <MyBox isLoading={loadingToolSelect}>
<Flex justify="center" py={8}> {loading ? (
<Text fontSize="sm" color="myGray.500"> <Flex justify="center" py={8}>
{t('common:Loading')} <Text fontSize="sm" color="myGray.500">
</Text> {t('common:Loading')}
</Flex> </Text>
) : availableTools.length === 0 ? ( </Flex>
<Box py={8} textAlign="center"> ) : availableTools.length === 0 ? (
<EmptyTip text="暂无可用工具" /> <Box py={8} textAlign="center">
<Text fontSize="sm" color="myGray.500" mt={3}> <EmptyTip text="暂无可用工具" />
<Text fontSize="sm" color="myGray.500" mt={3}>
</Text>
</Box> </Text>
) : ( </Box>
<VStack align="stretch" spacing={2}> ) : (
{availableTools.map((tool) => ( <VStack align="stretch" spacing={2}>
<Box {availableTools.map((tool) => (
key={tool.id} <Box
p={4} key={tool.id}
borderRadius="md" p={4}
cursor="pointer" borderRadius="md"
border="1px solid" cursor="pointer"
borderColor="gray.200" border="1px solid"
transition="all 0.2s" borderColor="gray.200"
_hover={{ transition="all 0.2s"
bg: 'blue.50', _hover={{
borderColor: 'blue.300' bg: 'blue.50',
}} borderColor: 'blue.300'
onClick={() => handleToolSelect(tool.id, !selectedToolIds.includes(tool.id))} }}
> onClick={() =>
<Flex align="center"> handleToolSelect(
<Checkbox tool.id,
size="md" !selectedTools.some((item) => item.pluginId === tool.id)
isChecked={selectedToolIds.includes(tool.id)} )
onChange={(e) => { }
e.stopPropagation(); >
handleToolSelect(tool.id, e.target.checked); <Flex align="center">
}} <Checkbox
mr={4} size="md"
colorScheme="blue" isChecked={selectedTools.some((item) => item.pluginId === tool.id)}
/> onChange={(e) => {
<Avatar src={tool.avatar} w="32px" h="32px" mr={3} /> e.stopPropagation();
<Box flex={1}> handleToolSelect(tool.id, e.target.checked);
<Text fontSize="md" fontWeight="medium" color="myGray.900"> }}
{tool.name} mr={4}
</Text> colorScheme="blue"
{tool.intro && ( />
<Text fontSize="sm" color="myGray.600" mt={1} noOfLines={2}> <Avatar src={tool.avatar} w="32px" h="32px" mr={3} />
{tool.intro} <Box flex={1}>
<Text fontSize="md" fontWeight="medium" color="myGray.900">
{tool.name}
</Text> </Text>
)} {tool.intro && (
</Box> <Text fontSize="sm" color="myGray.600" mt={1} noOfLines={2}>
</Flex> {tool.intro}
</Box> </Text>
))} )}
</VStack> </Box>
)} </Flex>
</Box>
))}
</VStack>
)}
{selectedToolIds.length > 0 && ( {selectedTools.length > 0 && (
<Box mt={4} p={3} bg="blue.50" borderRadius="md"> <Box mt={4} p={3} bg="blue.50" borderRadius="md">
<Text fontSize="sm" color="blue.700"> <Text fontSize="sm" color="blue.700">
{selectedToolIds.length} {selectedTools.length}
</Text> </Text>
</Box> </Box>
)} )}
</MyBox>
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
</Modal> </Modal>
</> </>
); ) : null;
}; };
export default React.memo(GateToolSelect); export default React.memo(GateToolSelect);

View File

@ -1,16 +1,10 @@
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type'; import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import { streamFetch } from '@/web/common/api/fetch'; import { streamFetch } from '@/web/common/api/fetch';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn, useSafeState } from 'ahooks';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import type { AppDetailType, AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import dynamic from 'next/dynamic';
import { Box } from '@chakra-ui/react';
import type { AppChatConfigType, AppDetailType } from '@fastgpt/global/core/app/type';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox'; import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
@ -20,29 +14,39 @@ import { useTranslation } from 'next-i18next';
import { ChatContext } from '@/web/core/chat/context/chatContext'; import { ChatContext } from '@/web/core/chat/context/chatContext';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils'; import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt'; import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { form2AppWorkflow } from '@/web/core/app/utils';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox')); import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
import { listQuickApps } from '@/web/support/user/team/gate/quickApp';
export const useChatGate = ({ export const useChatGate = ({
selectedToolIds, appForm,
onSelectedToolIdsChange,
nodes,
edges,
chatConfig,
isReady, isReady,
appDetail appDetail
}: { }: {
selectedToolIds?: string[]; appForm: AppSimpleEditFormType;
onSelectedToolIdsChange?: (toolIds: string[]) => void;
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
chatConfig: AppChatConfigType;
isReady: boolean; isReady: boolean;
appDetail: AppDetailType; appDetail: AppDetailType;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { setChatId, chatId, appId } = useChatStore(); const { setChatId, chatId, appId } = useChatStore();
const [selectedTools, setSelectedTools] = useState<FlowNodeTemplateType[]>([]);
const [workflowData, setWorkflowData] = useSafeState({
nodes: appDetail.modules || [],
edges: appDetail.edges || []
});
useEffect(() => {
const { nodes, edges } = form2AppWorkflow(
{
...appForm,
selectedTools
},
t
);
setWorkflowData({ nodes, edges });
}, [appForm, selectedTools, setWorkflowData, t]);
const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle); const onUpdateHistoryTitle = useContextSelector(ChatContext, (v) => v.onUpdateHistoryTitle);
const startChat = useMemoizedFn( const startChat = useMemoizedFn(
@ -61,19 +65,18 @@ export const useChatGate = ({
data: { data: {
// Send histories and user messages // Send histories and user messages
messages: histories, messages: histories,
nodes, nodes: workflowData.nodes,
edges, edges: workflowData.edges,
variables, variables,
responseChatItemId, responseChatItemId,
appId, appId,
appName: t('chat:chat_gate_app', { name: appDetail.name }), appName: t('chat:chat_gate_app', { name: appDetail.name }),
chatId, chatId,
chatConfig, chatConfig: appForm.chatConfig,
metadata: { metadata: {
source: 'web', source: 'web',
userAgent: navigator.userAgent userAgent: navigator.userAgent
}, }
selectedToolIds: selectedToolIds || []
}, },
onMessage: generatingMessage, onMessage: generatingMessage,
abortCtrl: controller abortCtrl: controller
@ -99,22 +102,18 @@ export const useChatGate = ({
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables); const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
const clearChatRecords = useContextSelector(ChatItemContext, (v) => v.clearChatRecords); const clearChatRecords = useContextSelector(ChatItemContext, (v) => v.clearChatRecords);
const pluginInputs = useMemo(() => {
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
}, [nodes]);
// Set chat box data // Set chat box data
useEffect(() => { useEffect(() => {
setChatBoxData({ setChatBoxData({
userAvatar: userInfo?.avatar, userAvatar: userInfo?.avatar,
appId: appId, appId: appId,
app: { app: {
chatConfig, chatConfig: appForm.chatConfig,
name: appDetail.name, name: appDetail.name,
avatar: appDetail.avatar, avatar: appDetail.avatar,
intro: appDetail.intro, intro: appDetail.intro,
type: appDetail.type, type: appDetail.type,
pluginInputs pluginInputs: []
} }
}); });
}, [ }, [
@ -122,9 +121,8 @@ export const useChatGate = ({
appDetail.intro, appDetail.intro,
appDetail.name, appDetail.name,
appDetail.type, appDetail.type,
appForm.chatConfig,
appId, appId,
chatConfig,
pluginInputs,
setChatBoxData, setChatBoxData,
userInfo?.avatar userInfo?.avatar
]); ]);
@ -151,29 +149,24 @@ export const useChatGate = ({
setChatId(); setChatId();
}, [clearChatRecords, setChatId]); }, [clearChatRecords, setChatId]);
const CustomChatContainer = useMemoizedFn(() => // 精选应用
appDetail.type === AppTypeEnum.plugin ? ( const { data: recommendApps = [] } = useRequest2(listQuickApps, {
<Box p={5} pb={16}> manual: false
<PluginRunBox });
appId={appId}
chatId={chatId} const CustomChatContainer = useMemoizedFn(() => (
onNewChat={restartChat} <ChatBox
onStartChat={startChat} isReady={isReady}
/> appId={appId}
</Box> chatId={chatId}
) : ( showMarkIcon
<ChatBox chatType={'chat'}
isReady={isReady} onStartChat={startChat}
appId={appId} selectedTools={selectedTools}
chatId={chatId} onSelectTools={setSelectedTools}
showMarkIcon recommendApps={recommendApps}
chatType={'chat'} />
onStartChat={startChat} ));
selectedToolIds={selectedToolIds}
onSelectedToolIdsChange={onSelectedToolIdsChange}
/>
)
);
return { return {
ChatContainer: CustomChatContainer, ChatContainer: CustomChatContainer,

View File

@ -29,9 +29,9 @@ type TabType = 'home' | 'copyright' | 'app' | 'logs';
const GatewayConfig = () => { const GatewayConfig = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [gateConfig, setGateConfig] = useState<GateSchemaType | undefined>(undefined); const [gateConfig, setGateConfig] = useState<GateSchemaType>();
// 添加 appForm 状态 // 添加 appForm 状态
const [appForm, setAppForm] = useState<AppSimpleEditFormType | undefined>(undefined); const [appForm, setAppForm] = useState<AppSimpleEditFormType>();
//从 appForm 中获取 selectedTools的 id 组成 string 数组 //从 appForm 中获取 selectedTools的 id 组成 string 数组
//gateConfig?.tools 改成 //gateConfig?.tools 改成

View File

@ -64,7 +64,6 @@ export type Props = {
chatId: string; chatId: string;
chatConfig: AppChatConfigType; chatConfig: AppChatConfigType;
metadata?: Record<string, any>; metadata?: Record<string, any>;
selectedToolIds?: string[];
}; };
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
@ -81,8 +80,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
appId, appId,
chatConfig, chatConfig,
chatId, chatId,
metadata = {}, metadata = {}
selectedToolIds = []
} = req.body as Props; } = req.body as Props;
try { try {
if (!Array.isArray(nodes)) { if (!Array.isArray(nodes)) {
@ -91,47 +89,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!Array.isArray(edges)) { if (!Array.isArray(edges)) {
throw new Error('Edges is not array'); throw new Error('Edges is not array');
} }
//对边进行过滤只保留selectedToolIds中的边
console.log('selectedToolIds', selectedToolIds);
// 创建从 pluginId 到 nodeId 的映射
const pluginIdToNodeIdMap = new Map<string, string>();
nodes.forEach((node) => {
if (node.pluginId) {
pluginIdToNodeIdMap.set(node.pluginId, node.nodeId);
}
});
console.log('pluginIdToNodeIdMap', Object.fromEntries(pluginIdToNodeIdMap));
// 获取选中工具对应的 nodeId 集合
const selectedNodeIds = new Set<string>();
selectedToolIds.forEach((pluginId) => {
const nodeId = pluginIdToNodeIdMap.get(pluginId);
if (nodeId) {
selectedNodeIds.add(nodeId);
}
});
console.log('selectedNodeIds', Array.from(selectedNodeIds));
// 过滤边:保留第一个边和目标节点在选中工具中的边
const filteredEdges = edges.filter((edge, index) => {
// 保留第一个边
if (index === 0) {
return true;
}
// 保留目标节点在选中工具中的边
return selectedNodeIds.has(edge.target);
});
console.log('Original edges count:', edges.length);
console.log('Filtered edges count:', filteredEdges.length);
console.log('Filtered edges:', filteredEdges);
// 使用过滤后的边
edges = filteredEdges;
const chatMessages = GPTMessages2Chats(messages); const chatMessages = GPTMessages2Chats(messages);
// console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length); // console.log(JSON.stringify(chatMessages, null, 2), '====', chatMessages.length);

View File

@ -10,9 +10,6 @@ import { useTranslation } from 'next-i18next';
import FoldButton from '@/pageComponents/chat/gatechat/FoldButton'; import FoldButton from '@/pageComponents/chat/gatechat/FoldButton';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type'; import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
import PageContainer from '@/components/PageContainer'; import PageContainer from '@/components/PageContainer';
import SideBar from '@/components/SideBar';
import ChatHistorySlider from '@/pageComponents/chat/ChatHistorySlider';
import SliderApps from '@/pageComponents/chat/SliderApps';
import ChatHeader from '@/pageComponents/chat/ChatHeader'; import ChatHeader from '@/pageComponents/chat/ChatHeader';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { serviceSideProps } from '@/web/common/i18n/utils'; import { serviceSideProps } from '@/web/common/i18n/utils';