Archer e009be51e7
Aiproxy (#3649)
* model config

* feat: model config ui

* perf: rename variable

* feat: custom request url

* perf: model buffer

* perf: init model

* feat: json model config

* auto login

* fix: ts

* update packages

* package

* fix: dockerfile
2025-01-22 22:59:28 +08:00

1017 lines
36 KiB
TypeScript

import {
Box,
Flex,
HStack,
Table,
TableContainer,
Tbody,
Td,
Th,
Thead,
Tr,
Switch,
ModalBody,
Input,
ModalFooter,
Button,
useDisclosure
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import {
ModelProviderList,
ModelProviderIdType,
getModelProvider
} from '@fastgpt/global/core/ai/provider';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { modelTypeList, ModelTypeEnum } from '@fastgpt/global/core/ai/model';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import dynamic from 'next/dynamic';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import {
deleteSystemModel,
getModelConfigJson,
getSystemModelDetail,
getSystemModelList,
putSystemModel
} from '@/web/core/ai/config';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { SystemModelItemType } from '@fastgpt/service/core/ai/type';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import { useForm } from 'react-hook-form';
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
import MyTextarea from '@/components/common/Textarea/MyTextarea';
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
import { clientInitData } from '@/web/common/system/staticData';
import { useUserStore } from '@/web/support/user/useUserStore';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { putUpdateWithJson } from '@/web/core/ai/config';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore();
const isRoot = userInfo?.username === 'root';
const [provider, setProvider] = useState<ModelProviderIdType | ''>('');
const providerList = useRef<{ label: any; value: ModelProviderIdType | '' }[]>([
{ label: t('common:common.All'), value: '' },
...ModelProviderList.map((item) => ({
label: (
<HStack>
<Avatar src={item.avatar} w={'1rem'} />
<Box>{t(item.name as any)}</Box>
</HStack>
),
value: item.id
}))
]);
const [modelType, setModelType] = useState<ModelTypeEnum | ''>('');
const selectModelTypeList = useRef<{ label: string; value: ModelTypeEnum | '' }[]>([
{ label: t('common:common.All'), value: '' },
...modelTypeList.map((item) => ({ label: t(item.label), value: item.value }))
]);
const [search, setSearch] = useState('');
const [showActive, setShowActive] = useState(false);
const {
data: systemModelList = [],
runAsync: refreshSystemModelList,
loading: loadingModels
} = useRequest2(getSystemModelList, {
manual: false
});
const refreshModels = useCallback(async () => {
clientInitData();
refreshSystemModelList();
}, [refreshSystemModelList]);
const modelList = useMemo(() => {
const formatLLMModelList = systemModelList
.filter((item) => item.type === ModelTypeEnum.llm)
.map((item) => ({
...item,
typeLabel: t('common:model.type.chat'),
priceLabel:
typeof item.inputPrice === 'number' ? (
<Box>
<Flex>
{`${t('common:common.Input')}:`}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5} ml={2}>
{item.inputPrice || 0}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
<Flex>
{`${t('common:common.Output')}:`}
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5} ml={2}>
{item.outputPrice || 0}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
</Box>
) : (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
),
tagColor: 'blue'
}));
const formatVectorModelList = systemModelList
.filter((item) => item.type === ModelTypeEnum.embedding)
.map((item) => ({
...item,
typeLabel: t('common:model.type.embedding'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
</Flex>
),
tagColor: 'yellow'
}));
const formatAudioSpeechModelList = systemModelList
.filter((item) => item.type === ModelTypeEnum.tts)
.map((item) => ({
...item,
typeLabel: t('common:model.type.tts'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 1K ${t('common:unit.character')}`}
</Flex>
),
tagColor: 'green'
}));
const formatWhisperModel = systemModelList
.filter((item) => item.type === ModelTypeEnum.stt)
.map((item) => ({
...item,
typeLabel: t('common:model.type.stt'),
priceLabel: (
<Flex color={'myGray.700'}>
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
{item.charsPointsPrice}
</Box>
{` ${t('common:support.wallet.subscription.point')} / 60${t('common:unit.seconds')}`}
</Flex>
),
tagColor: 'purple'
}));
const formatRerankModelList = systemModelList
.filter((item) => item.type === ModelTypeEnum.rerank)
.map((item) => ({
...item,
typeLabel: t('common:model.type.reRank'),
priceLabel: <Flex color={'myGray.700'}>- </Flex>,
tagColor: 'red'
}));
const list = (() => {
if (modelType === ModelTypeEnum.llm) return formatLLMModelList;
if (modelType === ModelTypeEnum.embedding) return formatVectorModelList;
if (modelType === ModelTypeEnum.tts) return formatAudioSpeechModelList;
if (modelType === ModelTypeEnum.stt) return formatWhisperModel;
if (modelType === ModelTypeEnum.rerank) return formatRerankModelList;
return [
...formatLLMModelList,
...formatVectorModelList,
...formatAudioSpeechModelList,
...formatWhisperModel,
...formatRerankModelList
];
})();
const formatList = list.map((item) => {
const provider = getModelProvider(item.provider);
return {
...item,
avatar: provider.avatar,
providerId: provider.id,
providerName: t(provider.name as any),
order: provider.order
};
});
formatList.sort((a, b) => a.order - b.order);
const filterList = formatList.filter((item) => {
const providerFilter = provider ? item.providerId === provider : true;
const regx = new RegExp(search, 'i');
const nameFilter = search ? regx.test(item.name) : true;
const activeFilter = showActive ? item.isActive : true;
return providerFilter && nameFilter && activeFilter;
});
return filterList;
}, [systemModelList, t, modelType, provider, search, showActive]);
const activeModelLength = useMemo(() => {
return modelList.filter((item) => item.isActive).length;
}, [modelList]);
const filterProviderList = useMemo(() => {
const allProviderIds: string[] = systemModelList.map((model) => model.provider);
return providerList.current.filter(
(item) => allProviderIds.includes(item.value) || item.value === ''
);
}, [systemModelList]);
const { runAsync: updateModel, loading: updatingModel } = useRequest2(putSystemModel, {
onSuccess: refreshModels
});
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: t('account:model.delete_model_confirm')
});
const { runAsync: deleteModel } = useRequest2(deleteSystemModel, {
onSuccess: refreshModels
});
const [editModelData, setEditModelData] = useState<SystemModelItemType>();
const { runAsync: onEditModel, loading: loadingData } = useRequest2(
(modelId: string) => getSystemModelDetail(modelId),
{
onSuccess: (data: SystemModelItemType) => {
setEditModelData(data);
}
}
);
const onCreateModel = (type: ModelTypeEnum) => {
const defaultModel = (() => {
if (type === ModelTypeEnum.llm) return llmModelList[0];
if (type === ModelTypeEnum.embedding) return embeddingModelList[0];
if (type === ModelTypeEnum.tts) return ttsModelList[0];
if (type === ModelTypeEnum.stt) return sttModelList[0];
if (type === ModelTypeEnum.rerank) return reRankModelList[0];
return llmModelList[0];
})();
setEditModelData({
...defaultModel,
model: '',
name: '',
charsPointsPrice: 0,
inputPrice: 0,
outputPrice: 0,
isCustom: true,
isActive: true,
// @ts-ignore
type
});
};
const {
isOpen: isOpenJsonConfig,
onOpen: onOpenJsonConfig,
onClose: onCloseJsonConfig
} = useDisclosure();
const isLoading = loadingModels || loadingData || updatingModel;
return (
<>
{isRoot && (
<Flex alignItems={'center'}>
{Tab}
<Box flex={1} />
<Button variant={'whiteBase'} mr={2} onClick={onOpenJsonConfig}>
{t('account:model.json_config')}
</Button>
<MyMenu
trigger="hover"
size="sm"
Button={<Button>{t('account:create_model')}</Button>}
menuList={[
{
children: [
{
label: t('common:model.type.chat'),
onClick: () => onCreateModel(ModelTypeEnum.llm)
},
{
label: t('common:model.type.embedding'),
onClick: () => onCreateModel(ModelTypeEnum.embedding)
},
{
label: t('common:model.type.tts'),
onClick: () => onCreateModel(ModelTypeEnum.tts)
},
{
label: t('common:model.type.stt'),
onClick: () => onCreateModel(ModelTypeEnum.stt)
},
{
label: t('common:model.type.reRank'),
onClick: () => onCreateModel(ModelTypeEnum.rerank)
}
]
}
]}
/>
</Flex>
)}
<MyBox flex={'1 0 0'} isLoading={isLoading}>
<Flex flexDirection={'column'} h={'100%'}>
<Flex>
<HStack flexShrink={0}>
<Box fontSize={'sm'} color={'myGray.900'}>
{t('common:model.provider')}
</Box>
<MySelect
w={'200px'}
bg={'myGray.50'}
value={provider}
onchange={setProvider}
list={filterProviderList}
/>
</HStack>
<HStack flexShrink={0} ml={6}>
<Box fontSize={'sm'} color={'myGray.900'}>
{t('common:model.model_type')}
</Box>
<MySelect
w={'150px'}
bg={'myGray.50'}
value={modelType}
onchange={setModelType}
list={selectModelTypeList.current}
/>
</HStack>
<Box flex={1} />
<Box flex={'0 0 250px'}>
<SearchInput
bg={'myGray.50'}
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder={t('common:model.search_name_placeholder')}
/>
</Box>
</Flex>
<TableContainer mt={5} flex={'1 0 0'} h={0} overflowY={'auto'}>
<Table>
<Thead>
<Tr color={'myGray.600'}>
<Th fontSize={'xs'}>{t('common:model.name')}</Th>
<Th fontSize={'xs'}>{t('common:model.model_type')}</Th>
<Th fontSize={'xs'}>{t('common:model.billing')}</Th>
<Th fontSize={'xs'}>
<Box
cursor={'pointer'}
onClick={() => setShowActive(!showActive)}
color={showActive ? 'primary.600' : 'myGray.600'}
>
{t('account:model.active')}({activeModelLength})
</Box>
</Th>
<Th fontSize={'xs'}></Th>
</Tr>
</Thead>
<Tbody>
{modelList.map((item, index) => (
<Tr key={item.model} _hover={{ bg: 'myGray.50' }}>
<Td fontSize={'sm'}>
<HStack>
<Avatar src={item.avatar} w={'1.2rem'} />
<Box color={'myGray.900'}>{item.name}</Box>
</HStack>
</Td>
<Td>
<MyTag colorSchema={item.tagColor as any}>{item.typeLabel}</MyTag>
</Td>
<Td fontSize={'sm'}>{item.priceLabel}</Td>
<Td fontSize={'sm'}>
<Switch
size={'sm'}
isChecked={item.isActive}
onChange={(e) =>
updateModel({
model: item.model,
metadata: { isActive: e.target.checked }
})
}
colorScheme={'myBlue'}
/>
</Td>
<Td>
<HStack>
<MyIconButton
icon={'common/settingLight'}
onClick={() => onEditModel(item.model)}
/>
{item.isCustom && (
<MyIconButton
icon={'delete'}
hoverColor={'red.500'}
onClick={() => openConfirm(() => deleteModel({ model: item.model }))()}
/>
)}
</HStack>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Flex>
</MyBox>
<ConfirmModal />
{!!editModelData && (
<ModelEditModal
modelData={editModelData}
onSuccess={refreshModels}
onClose={() => setEditModelData(undefined)}
/>
)}
{isOpenJsonConfig && (
<JsonConfigModal onClose={onCloseJsonConfig} onSuccess={refreshModels} />
)}
</>
);
};
const InputStyles = {
maxW: '300px',
bg: 'myGray.50',
w: '100%',
rows: 3
};
const ModelEditModal = ({
modelData,
onSuccess,
onClose
}: {
modelData: SystemModelItemType;
onSuccess: () => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const { register, getValues, setValue, handleSubmit, watch } = useForm<SystemModelItemType>({
defaultValues: modelData
});
const isCustom = !!modelData.isCustom;
const isLLMModel = modelData?.type === ModelTypeEnum.llm;
const isEmbeddingModel = modelData?.type === ModelTypeEnum.embedding;
const isTTSModel = modelData?.type === ModelTypeEnum.tts;
const isSTTModel = modelData?.type === ModelTypeEnum.stt;
const isRerankModel = modelData?.type === ModelTypeEnum.rerank;
const provider = watch('provider');
const providerData = useMemo(() => getModelProvider(provider), [provider]);
const providerList = useRef<{ label: any; value: ModelProviderIdType }[]>(
ModelProviderList.map((item) => ({
label: (
<HStack>
<Avatar src={item.avatar} w={'1rem'} />
<Box>{t(item.name as any)}</Box>
</HStack>
),
value: item.id
}))
);
const priceUnit = useMemo(() => {
if (isLLMModel || isEmbeddingModel) return '/ 1k Tokens';
if (isTTSModel) return `/ 1k ${t('common:unit.character')}`;
if (isSTTModel) return `/ 60 ${t('common:unit.seconds')}`;
return '';
return '';
}, [isLLMModel, isEmbeddingModel, isTTSModel, t, isSTTModel]);
const { runAsync: updateModel, loading: updatingModel } = useRequest2(
async (data: SystemModelItemType) => {
return putSystemModel({
model: data.model,
metadata: data
}).then(onSuccess);
},
{
onSuccess: () => {
onClose();
},
successToast: t('common:common.Success')
}
);
return (
<MyModal
iconSrc={'modal/edit'}
title={t('account:model.edit_model')}
isOpen
onClose={onClose}
maxW={['90vw', '80vw']}
w={'100%'}
h={'100%'}
>
<ModalBody>
<Flex gap={4}>
<TableContainer flex={'1'}>
<Table>
<Thead>
<Tr color={'myGray.600'}>
<Th fontSize={'xs'}>{t('account:model.param_name')}</Th>
<Th fontSize={'xs'}></Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>{t('common:model.provider')}</Td>
<Td textAlign={'right'}>
{isCustom ? (
<MySelect
value={provider}
onchange={(value) => setValue('provider', value)}
list={providerList.current}
{...InputStyles}
/>
) : (
<HStack justifyContent={'flex-end'}>
<Avatar src={providerData.avatar} w={'1rem'} />
<Box>{t(providerData.name)}</Box>
</HStack>
)}
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.model_id')}</Box>
<QuestionTip label={t('account:model.model_id_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
{isCustom ? (
<Input {...register('model', { required: true })} {...InputStyles} />
) : (
modelData?.model
)}
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.alias')}</Box>
<QuestionTip label={t('account:model.alias_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Input {...register('name', { required: true })} {...InputStyles} />
</Td>
</Tr>
{priceUnit && feConfigs?.isPlus && (
<>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.charsPointsPrice')}</Box>
<QuestionTip label={t('account:model.charsPointsPrice_tip')} />
</HStack>
</Td>
<Td>
<Flex justify="flex-end">
<HStack w={'100%'} maxW={'300px'}>
<MyNumberInput
flex={'1 0 0'}
register={register}
name={'charsPointsPrice'}
step={0.01}
/>
<Box fontSize={'sm'}>{priceUnit}</Box>
</HStack>
</Flex>
</Td>
</Tr>
{isLLMModel && (
<>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.input_price')}</Box>
<QuestionTip label={t('account:model.input_price_tip')} />
</HStack>
</Td>
<Td>
<Flex justify="flex-end">
<HStack w={'100%'} maxW={'300px'}>
<MyNumberInput
flex={'1 0 0'}
register={register}
name={'inputPrice'}
step={0.01}
/>
<Box fontSize={'sm'}>{priceUnit}</Box>
</HStack>
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.output_price')}</Box>
<QuestionTip label={t('account:model.output_price_tip')} />
</HStack>
</Td>
<Td>
<Flex justify="flex-end">
<HStack w={'100%'} maxW={'300px'}>
<MyNumberInput
flex={'1 0 0'}
register={register}
name={'outputPrice'}
step={0.01}
/>
<Box fontSize={'sm'}>{priceUnit}</Box>
</HStack>
</Flex>
</Td>
</Tr>
</>
)}
</>
)}
{isLLMModel && (
<>
<Tr>
<Td>{t('common:core.ai.Max context')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput register={register} name="maxContext" {...InputStyles} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.max_quote')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput
register={register}
name="quoteMaxToken"
{...InputStyles}
/>
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('common:core.chat.response.module maxToken')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput register={register} name="maxResponse" {...InputStyles} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.max_temperature')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput
register={register}
name="maxTemperature"
step={0.1}
{...InputStyles}
/>
</Flex>
</Td>
</Tr>
</>
)}
{isEmbeddingModel && (
<>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.default_token')}</Box>
<QuestionTip label={t('account:model.default_token_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput register={register} name="defaultToken" {...InputStyles} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('common:core.ai.Max context')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput register={register} name="maxToken" {...InputStyles} />
</Flex>
</Td>
</Tr>
</>
)}
{isTTSModel && (
<>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.voices')}</Box>
<QuestionTip label={t('account:model.voices_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<JsonEditor
value={JSON.stringify(getValues('voices'), null, 2)}
onChange={(e) => {
try {
setValue('voices', JSON.parse(e));
} catch (error) {
console.error(error);
}
}}
{...InputStyles}
/>
</Flex>
</Td>
</Tr>
</>
)}
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.request_url')}</Box>
<QuestionTip label={t('account:model.request_url_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Input {...register('requestUrl')} {...InputStyles} />
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.request_auth')}</Box>
<QuestionTip label={t('account:model.request_auth_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Input {...register('requestAuth')} {...InputStyles} />
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
{isLLMModel && (
<TableContainer flex={'1'}>
<Table>
<Thead>
<Tr color={'myGray.600'}>
<Th fontSize={'xs'}>{t('account:model.param_name')}</Th>
<Th fontSize={'xs'}></Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.tool_choice')}</Box>
<QuestionTip label={t('account:model.tool_choice_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('toolChoice')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.function_call')}</Box>
<QuestionTip label={t('account:model.function_call_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('functionCall')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.vision')}</Box>
<QuestionTip label={t('account:model.vision_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('vision')} />
</Flex>
</Td>
</Tr>
{feConfigs?.isPlus && (
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.censor')}</Box>
<QuestionTip label={t('account:model.censor_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('censor')} />
</Flex>
</Td>
</Tr>
)}
<Tr>
<Td>{t('account:model.dataset_process')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('datasetProcess')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.used_in_classify')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('usedInClassify')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.used_in_extract_fields')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('usedInExtractFields')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.used_in_tool_call')}</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('usedInToolCall')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.default_system_chat_prompt')}</Box>
<QuestionTip label={t('account:model.default_system_chat_prompt_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<MyTextarea {...register('defaultSystemChatPrompt')} {...InputStyles} />
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.custom_cq_prompt')}</Box>
<QuestionTip label={t('account:model.custom_cq_prompt_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<MyTextarea {...register('customCQPrompt')} {...InputStyles} />
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.custom_extract_prompt')}</Box>
<QuestionTip label={t('account:model.custom_extract_prompt_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<MyTextarea {...register('customExtractPrompt')} {...InputStyles} />
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.default_config')}</Box>
<QuestionTip label={t('account:model.default_config_tip')} />
</HStack>
</Td>
<Td textAlign={'right'}>
<JsonEditor
value={JSON.stringify(getValues('defaultConfig'), null, 2)}
resize
onChange={(e) => {
try {
setValue('defaultConfig', JSON.parse(e));
} catch (error) {
console.error(error);
}
}}
{...InputStyles}
/>
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
)}
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} mr={4} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button isLoading={updatingModel} onClick={handleSubmit(updateModel)}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
const JsonConfigModal = ({
onClose,
onSuccess
}: {
onClose: () => void;
onSuccess: () => void;
}) => {
const { t } = useTranslation();
const [data, setData] = useState<string>('');
const { loading } = useRequest2(getModelConfigJson, {
manual: false,
onSuccess(res) {
setData(res);
}
});
const { openConfirm, ConfirmModal } = useConfirm({
content: t('account:model.json_config_confirm')
});
const { runAsync } = useRequest2(putUpdateWithJson, {
onSuccess: () => {
onSuccess();
onClose();
}
});
return (
<MyModal
isOpen
isLoading={loading}
onClose={onClose}
iconSrc="modal/edit"
title={t('account:model.json_config')}
w={'100%'}
h={'100%'}
>
<ModalBody display={'flex'} flexDirection={'column'}>
<Box fontSize={'sm'} color={'myGray.500'}>
{t('account:model.json_config_tip')}
</Box>
<Box mt={2} flex={1} w={'100%'} overflow={'hidden'}>
<JsonEditor value={data} onChange={setData} resize h={'100%'} />
</Box>
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} mr={4} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button
onClick={() =>
openConfirm(() => {
return runAsync({ config: data });
})()
}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
<ConfirmModal />
</MyModal>
);
};
export default ModelTable;