import { useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import type { BackendModel, FormValue, ProviderConfigModal, ProviderEnum, } from './declarations' import ModelSelector from './model-selector' import ModelCard from './model-card' import ModelItem from './model-item' import ModelModal from './model-modal' import config from './configs' import { ConfigurableProviders } from './utils' import { ChevronDownDouble } from '@/app/components/base/icons/src/vender/line/arrows' import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general' import { changeModelProviderPriority, deleteModelProvider, deleteModelProviderModel, fetchDefaultModal, fetchModelProviders, setModelProvider, updateDefaultModel, } from '@/service/common' import { useToastContext } from '@/app/components/base/toast' import Confirm from '@/app/components/base/confirm/common' import { ModelType } from '@/app/components/header/account-setting/model-page/declarations' import { useEventEmitterContextContext } from '@/context/event-emitter' import { useProviderContext } from '@/context/provider-context' import Tooltip from '@/app/components/base/tooltip' import I18n from '@/context/i18n' const MODEL_CARD_LIST = [ config.openai, config.anthropic, ] const titleClassName = ` flex items-center h-9 text-sm font-medium text-gray-900 ` const tipClassName = ` ml-0.5 w-[14px] h-[14px] text-gray-400 ` type DeleteModel = { model_name: string model_type: string } const ModelPage = () => { const { t } = useTranslation() const { locale } = useContext(I18n) const { updateModelList, embeddingsDefaultModel, mutateEmbeddingsDefaultModel, speech2textDefaultModel, mutateSpeech2textDefaultModel, } = useProviderContext() const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders) const { data: textGenerationDefaultModel, mutate: mutateTextGenerationDefaultModel } = useSWR('/workspaces/current/default-model?model_type=text-generation', fetchDefaultModal) const [showMoreModel, setShowMoreModel] = useState(false) const [showModal, setShowModal] = useState(false) const { notify } = useToastContext() const { eventEmitter } = useEventEmitterContextContext() const [modelModalConfig, setModelModalConfig] = useState(undefined) const [confirmShow, setConfirmShow] = useState(false) const [deleteModel, setDeleteModel] = useState() const [modalMode, setModalMode] = useState('add') let modelList = [] if (locale === 'en') { modelList = [ config.azure_openai, config.replicate, config.huggingface_hub, config.zhipuai, config.spark, config.minimax, config.tongyi, config.wenxin, config.chatglm, config.xinference, config.openllm, config.localai, ] } else { modelList = [ config.huggingface_hub, config.zhipuai, config.spark, config.minimax, config.azure_openai, config.replicate, config.tongyi, config.wenxin, config.chatglm, config.xinference, config.openllm, config.localai, ] } const handleOpenModal = (newModelModalConfig: ProviderConfigModal | undefined, editValue?: FormValue) => { if (newModelModalConfig) { setShowModal(true) const defaultValue = editValue ? { ...newModelModalConfig.defaultValue, ...editValue } : newModelModalConfig.defaultValue setModelModalConfig({ ...newModelModalConfig, defaultValue, }) if (editValue) setModalMode('edit') else setModalMode('add') } } const handleCancelModal = () => { setShowModal(false) } const handleUpdateProvidersAndModelList = () => { updateModelList(ModelType.textGeneration) updateModelList(ModelType.embeddings) updateModelList(ModelType.speech2text) mutateProviders() } const handleSave = async (originValue?: FormValue) => { if (originValue && modelModalConfig) { const v = modelModalConfig.filterValue ? modelModalConfig.filterValue(originValue) : originValue let body, url if (ConfigurableProviders.includes(modelModalConfig.key)) { const { model_name, model_type, ...config } = v body = { model_name, model_type, config, } url = `/workspaces/current/model-providers/${modelModalConfig.key}/models` } else { body = { config: v, } url = `/workspaces/current/model-providers/${modelModalConfig.key}` } try { eventEmitter?.emit('provider-save') const res = await setModelProvider({ url, body }) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) handleUpdateProvidersAndModelList() handleCancelModal() } eventEmitter?.emit('') } catch (e) { eventEmitter?.emit('') } } } const handleConfirm = (deleteModel: DeleteModel, providerKey: ProviderEnum) => { setDeleteModel({ ...deleteModel, providerKey }) setConfirmShow(true) } const handleOperate = async ({ type, value }: Record, provierKey: ProviderEnum) => { if (type === 'delete') { if (!value) { const res = await deleteModelProvider({ url: `/workspaces/current/model-providers/${provierKey}` }) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) handleUpdateProvidersAndModelList() } } else { handleConfirm(value, provierKey) } } if (type === 'priority') { const res = await changeModelProviderPriority({ url: `/workspaces/current/model-providers/${provierKey}/preferred-provider-type`, body: { preferred_provider_type: value, }, }) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) mutateProviders() } } } const handleDeleteModel = async () => { const { model_name, model_type, providerKey } = deleteModel || {} const res = await deleteModelProviderModel({ url: `/workspaces/current/model-providers/${providerKey}/models?model_name=${model_name}&model_type=${model_type}`, }) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) setConfirmShow(false) handleUpdateProvidersAndModelList() } } const mutateDefaultModel = (type: ModelType) => { if (type === ModelType.textGeneration) mutateTextGenerationDefaultModel() if (type === ModelType.embeddings) mutateEmbeddingsDefaultModel() if (type === ModelType.speech2text) mutateSpeech2textDefaultModel() } const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => { const res = await updateDefaultModel({ url: '/workspaces/current/default-model', body: { model_type: type, provider_name: v.model_provider.provider_name, model_name: v.model_name, }, }) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) mutateDefaultModel(type) } } return (
{t('common.modelProvider.systemReasoningModel.key')} {t('common.modelProvider.systemReasoningModel.tip')}
} >
handleChangeDefaultModel(ModelType.textGeneration, v)} />
{t('common.modelProvider.embeddingModel.key')} {t('common.modelProvider.embeddingModel.tip')}
} >
handleChangeDefaultModel(ModelType.embeddings, v)} />
{t('common.modelProvider.speechToTextModel.key')} {t('common.modelProvider.speechToTextModel.tip')}
} >
handleChangeDefaultModel(ModelType.speech2text, v)} />
{t('common.modelProvider.models')}
{ MODEL_CARD_LIST.map((model, index) => ( handleOpenModal(model.modal, editValue)} onOperate={v => handleOperate(v, model.item.key)} /> )) }
{ modelList.slice(0, showMoreModel ? modelList.length : 3).map((model, index) => ( handleOpenModal(model.modal, editValue)} onOperate={v => handleOperate(v, model.item.key)} onUpdate={mutateProviders} /> )) } { !showMoreModel && (
setShowMoreModel(true)}>
{t('common.modelProvider.showMoreModelProvider')}
) } setConfirmShow(false)} title={deleteModel?.model_name || ''} desc={t('common.modelProvider.item.deleteDesc', { modelName: deleteModel?.model_name }) || ''} onConfirm={handleDeleteModel} />
) } export default ModelPage