import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Flex, Grid, BoxProps, Textarea, useTheme, Table, Thead, Tbody, Tr, Th, Td, TableContainer, useDisclosure, Button, IconButton } from '@chakra-ui/react'; import { useUserStore } from '@/store/user'; import { useQuery } from '@tanstack/react-query'; import { QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons'; import { useForm, useFieldArray } from 'react-hook-form'; import { useGlobalStore } from '@/store/global'; import { appModules2Form, getDefaultAppForm, appForm2Modules, type EditFormType } from '@/utils/app'; import { chatModelList } from '@/store/static'; import { formatPrice } from '@/utils/user'; import { ChatModelSystemTip, ChatModelLimitTip, welcomeTextTip } from '@/constants/flow/ModuleTemplate'; import { AppModuleItemType, VariableItemType } from '@/types/app'; import { useRequest } from '@/hooks/useRequest'; import { useConfirm } from '@/hooks/useConfirm'; import { FlowModuleTypeEnum } from '@/constants/flow'; import { streamFetch } from '@/api/fetch'; import { useRouter } from 'next/router'; import { useToast } from '@/hooks/useToast'; import { AppSchema } from '@/types/mongoSchema'; import { delModelById } from '@/api/app'; import dynamic from 'next/dynamic'; import MySelect from '@/components/Select'; import MySlider from '@/components/Slider'; import MyTooltip from '@/components/MyTooltip'; import Avatar from '@/components/Avatar'; import MyIcon from '@/components/Icon'; import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; import { useTranslation } from 'react-i18next'; import { getSpecialModule } from '@/components/ChatBox/utils'; import { addVariable } from '../VariableEditModal'; import { KBSelectModal, KbParamsModal } from '../KBSelectModal'; const VariableEditModal = dynamic(() => import('../VariableEditModal')); const InfoModal = dynamic(() => import('../InfoModal')); const Settings = ({ appId }: { appId: string }) => { const theme = useTheme(); const router = useRouter(); const { t } = useTranslation(); const { toast } = useToast(); const { appDetail, updateAppDetail, loadKbList, myKbList } = useUserStore(); const { isPc } = useGlobalStore(); const [editVariable, setEditVariable] = useState(); const [settingAppInfo, setSettingAppInfo] = useState(); const [refresh, setRefresh] = useState(false); const { openConfirm: openConfirmSave, ConfirmModal: ConfirmSaveModal } = useConfirm({ content: t('app.Confirm Save App Tip') }); const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({ content: t('app.Confirm Del App Tip') }); const { register, setValue, getValues, reset, handleSubmit, control } = useForm({ defaultValues: getDefaultAppForm() }); const { fields: variables, append: appendVariable, remove: removeVariable, replace: replaceVariables } = useFieldArray({ control, name: 'variables' }); const { fields: kbList, replace: replaceKbList } = useFieldArray({ control, name: 'kb.list' }); const { isOpen: isOpenKbSelect, onOpen: onOpenKbSelect, onClose: onCloseKbSelect } = useDisclosure(); const { isOpen: isOpenKbParams, onOpen: onOpenKbParams, onClose: onCloseKbParams } = useDisclosure(); const chatModelSelectList = useMemo(() => { return chatModelList.map((item) => ({ value: item.model, label: `${item.name} (${formatPrice(item.price, 1000)} 元/1k tokens)` })); }, [refresh]); const tokenLimit = useMemo(() => { return ( chatModelList.find((item) => item.model === getValues('chatModel.model'))?.contextMaxToken || 4000 ); }, [getValues, refresh]); const selectedKbList = useMemo( () => myKbList.filter((item) => kbList.find((kb) => kb.kbId === item._id)), [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); setRefresh((state) => !state); }, [appDetail.modules, reset]); const { mutate: onSubmitSave, isLoading: isSaving } = useRequest({ mutationFn: async (data: EditFormType) => { const modules = appForm2Modules(data); await updateAppDetail(appDetail._id, { modules }); }, successToast: '保存成功', errorToast: '保存出现异常' }); useEffect(() => { appModule2Form(); }, [appModule2Form]); useQuery(['initkb', appId], () => loadKbList()); const BoxStyles: BoxProps = { bg: 'myWhite.200', px: 4, py: 3, borderRadius: 'lg', border: theme.borders.base }; const BoxBtnStyles: BoxProps = { cursor: 'pointer', px: 3, py: '2px', borderRadius: 'md', _hover: { bg: 'myGray.200' } }; const LabelStyles: BoxProps = { w: ['60px', '100px'], flexShrink: 0, fontSize: ['sm', 'md'] }; return ( 基础信息 {/* basic info */} {appDetail.name} } variant={'base'} borderRadius={'md'} aria-label={'delete'} _hover={{ bg: 'myGray.100', color: 'red.600' }} isLoading={isLoading} onClick={openConfirmDel(handleDelModel)} /> {appDetail.intro || '快来给应用一个介绍~'} 应用配置 {/* variable */} 变量 setEditVariable(addVariable())}> + 新增 {variables.map((item, index) => ( ))}
变量名 变量 key 必填
{item.label} {item.key} {item.required ? '✔' : ''} setEditVariable(item)} /> removeVariable(index)} />
{/* model */} AI 配置 对话模型 { 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) => ( router.push({ pathname: '/kb/detail', query: { kbId: item._id } }) } > {item.name} ))} {/* welcome */} 对话开场白