diff --git a/client/src/components/Icon/icons/back.svg b/client/src/components/Icon/icons/back.svg index 311943fbf..90a53a4e8 100644 --- a/client/src/components/Icon/icons/back.svg +++ b/client/src/components/Icon/icons/back.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/client/src/constants/flow/ModuleTemplate.ts b/client/src/constants/flow/ModuleTemplate.ts index 3dece5a93..ff2371dcf 100644 --- a/client/src/constants/flow/ModuleTemplate.ts +++ b/client/src/constants/flow/ModuleTemplate.ts @@ -371,7 +371,11 @@ export const EmptyModule: FlowModuleTemplateType = { export const ModuleTemplates = [ { label: '输入模块', - list: [UserInputModule, HistoryModule, VariableModule, UserGuideModule] + list: [UserInputModule, HistoryModule] + }, + { + label: '引导模块', + list: [UserGuideModule, VariableModule] }, { label: '内容生成', diff --git a/client/src/pages/app/detail/components/AdEdit/components/TemplateList.tsx b/client/src/pages/app/detail/components/AdEdit/components/TemplateList.tsx index 22502cc07..fbd272de9 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/TemplateList.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/TemplateList.tsx @@ -1,21 +1,53 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Box, Flex } from '@chakra-ui/react'; import { ModuleTemplates } from '@/constants/flow/ModuleTemplate'; import { FlowModuleTemplateType } from '@/types/flow'; -import type { XYPosition } from 'reactflow'; +import type { Node, XYPosition } from 'reactflow'; import { useGlobalStore } from '@/store/global'; +import type { AppModuleItemType } from '@/types/app'; import Avatar from '@/components/Avatar'; +import { FlowModuleTypeEnum } from '@/constants/flow'; const ModuleTemplateList = ({ + nodes, isOpen, onAddNode, onClose }: { + nodes?: Node[]; isOpen: boolean; onAddNode: (e: { template: FlowModuleTemplateType; position: XYPosition }) => void; onClose: () => void; }) => { const { isPc } = useGlobalStore(); + + const filterTemplates = useMemo(() => { + const guideModulesIndex = ModuleTemplates.findIndex((item) => item.label === '引导模块'); + const guideModule: { + label: string; + list: FlowModuleTemplateType[]; + } = JSON.parse(JSON.stringify(ModuleTemplates[guideModulesIndex])); + + if (nodes?.find((item) => item.type === FlowModuleTypeEnum.userGuide)) { + const index = guideModule.list.findIndex( + (item) => item.flowType === FlowModuleTypeEnum.userGuide + ); + guideModule.list.splice(index, 1); + } + if (nodes?.find((item) => item.type === FlowModuleTypeEnum.variable)) { + const index = guideModule.list.findIndex( + (item) => item.flowType === FlowModuleTypeEnum.variable + ); + guideModule.list.splice(index, 1); + } + + return [ + ...ModuleTemplates.slice(0, guideModulesIndex), + guideModule, + ...ModuleTemplates.slice(guideModulesIndex + 1) + ]; + }, [nodes]); + return ( <> - {ModuleTemplates.map((item) => + {filterTemplates.map((item) => item.list.map((item) => ( { if (isPc) return; + onClose(); onAddNode({ template: item, position: { x: e.clientX, y: e.clientY } }); - onClose(); }} > diff --git a/client/src/pages/app/detail/components/AdEdit/index.tsx b/client/src/pages/app/detail/components/AdEdit/index.tsx index 381fe1c49..52ba0076f 100644 --- a/client/src/pages/app/detail/components/AdEdit/index.tsx +++ b/client/src/pages/app/detail/components/AdEdit/index.tsx @@ -95,12 +95,12 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { const { x, y, zoom } = useViewport(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); - const [loaded, setLoaded] = useState(false); const { isOpen: isOpenTemplate, onOpen: onOpenTemplate, onClose: onCloseTemplate } = useDisclosure(); + const [testModules, setTestModules] = useState(); const onFixView = useCallback(() => { @@ -111,6 +111,48 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { }, 100); }, []); + const flow2AppModules = useCallback(() => { + const modules: AppModuleItemType[] = nodes.map((item) => ({ + moduleId: item.data.moduleId, + position: item.position, + flowType: item.data.flowType, + inputs: item.data.inputs.map((item) => ({ + key: item.key, + value: item.value, + connected: item.type !== FlowInputItemTypeEnum.target + })), + outputs: item.data.outputs.map((item) => ({ + key: item.key, + targets: [] as FlowOutputTargetItemType[] + })) + })); + + // update inputs and outputs + modules.forEach((module) => { + module.inputs.forEach((input) => { + input.connected = + input.connected || + !!edges.find( + (edge) => edge.target === module.moduleId && edge.targetHandle === input.key + ); + }); + module.outputs.forEach((output) => { + output.targets = edges + .filter( + (edge) => + edge.source === module.moduleId && + edge.sourceHandle === output.key && + edge.targetHandle + ) + .map((edge) => ({ + moduleId: edge.target, + key: edge.targetHandle || '' + })); + }); + }); + return modules; + }, [edges, nodes]); + const onChangeNode = useCallback( ({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => { setNodes((nodes) => @@ -176,48 +218,6 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { }, [onChangeNode, onDelNode, setNodes, x, y, zoom] ); - const flow2AppModules = useCallback(() => { - const modules: AppModuleItemType[] = nodes.map((item) => ({ - moduleId: item.data.moduleId, - position: item.position, - flowType: item.data.flowType, - inputs: item.data.inputs.map((item) => ({ - key: item.key, - value: item.value, - connected: item.type !== FlowInputItemTypeEnum.target - })), - outputs: item.data.outputs.map((item) => ({ - key: item.key, - targets: [] as FlowOutputTargetItemType[] - })) - })); - - // update inputs and outputs - modules.forEach((module) => { - module.inputs.forEach((input) => { - input.connected = - input.connected || - !!edges.find( - (edge) => edge.target === module.moduleId && edge.targetHandle === input.key - ); - }); - module.outputs.forEach((output) => { - output.targets = edges - .filter( - (edge) => - edge.source === module.moduleId && - edge.sourceHandle === output.key && - edge.targetHandle - ) - .map((edge) => ({ - moduleId: edge.target, - key: edge.targetHandle || '' - })); - }); - }); - return modules; - }, [edges, nodes]); - const onDelConnect = useCallback( (id: string) => { setEdges((state) => state.filter((item) => item.id !== id)); @@ -274,7 +274,6 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { ) ); - setLoaded(true); onFixView(); }, [onDelConnect, setEdges, setNodes, onFixView, onChangeNode, onDelNode] @@ -296,10 +295,10 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { > {fullScreen ? ( <> - + } + icon={} borderRadius={'md'} borderColor={'myGray.300'} variant={'base'} @@ -425,7 +424,12 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => { - + 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(); - - useQuery(['initkb', appId], () => loadKbList()); + const [settingAppInfo, setSettingAppInfo] = useState(); 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 ( - + + 基础信息 + + {/* basic info */} + + + + + {appDetail.name} + + } + variant={'base'} + borderRadius={'md'} + aria-label={'delete'} + _hover={{ + bg: 'myGray.100', + color: 'red.600' + }} + isLoading={isLoading} + onClick={openConfirm(handleDelModel)} + /> + + + {appDetail.intro || '快来给应用一个介绍~'} + + + + + + + + + 应用配置 @@ -181,221 +287,219 @@ const Settings = ({ appId }: { appId: string }) => { - - {/* variable */} - - - - - 变量 - - setEditVariable(addVariable())}> - + 新增 - - - - - - - - - - - - - - - {variables.map((item, index) => ( - - - - - - - ))} - -
变量名变量 key必填
{item.label} {item.key}{item.required ? '✔' : ''} - setEditVariable(item)} - /> - removeVariable(index)} - /> -
-
+ + {/* variable */} + + + + + 变量 - - - - - - AI 配置 + setEditVariable(addVariable())}> + + 新增 - - - 对话模型 - { - 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); - }} - /> - - - 温度 - - { - setValue('chatModel.temperature', e); - setRefresh(!refresh); - }} - /> - - - - 回复上限 - - { - setValue('chatModel.maxToken', val); - setRefresh(!refresh); - }} - /> - - - - - 提示词 - - - - - - - - - 限定词 - - - - - - - - - {/* kb */} - - - - - 知识库 - - - - 选择 - - - - 参数 - - - - 相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')}, - 空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'} - - - {selectedKbList.map((item) => ( - - - - {item.name} - - - ))} - - - - {/* welcome */} - - - - 对话开场白 - - - - - +
+ + + 限定词 + + + + + + +
+ + {/* kb */} + + + + + 知识库 + + + + 选择 + + + + 参数 + + + + 相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')}, + 空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'} + + + {selectedKbList.map((item) => ( + + + + {item.name} + + + ))} + + + + {/* welcome */} + + + + 对话开场白 + + + + +