feat: add retriever rank fe (#1557)
Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Cohere, CohereText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'cohere',
|
||||
'zh-Hans': 'cohere',
|
||||
},
|
||||
icon: <Cohere className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.cohere,
|
||||
titleIcon: {
|
||||
'en': <CohereText className='w-[120px] h-6' />,
|
||||
'zh-Hans': <CohereText className='w-[120px] h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.cohere,
|
||||
title: {
|
||||
'en': 'cohere',
|
||||
'zh-Hans': 'cohere',
|
||||
},
|
||||
icon: <Cohere className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://dashboard.cohere.com/api-keys',
|
||||
label: {
|
||||
'en': 'Get your API key from cohere',
|
||||
'zh-Hans': '从 cohere 获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['api_key'],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API key here',
|
||||
'zh-Hans': '在此输入您的 API Key',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -13,6 +13,7 @@ import openllm from './openllm'
|
||||
import localai from './localai'
|
||||
import zhipuai from './zhipuai'
|
||||
import baichuan from './baichuan'
|
||||
import cohere from './cohere'
|
||||
|
||||
export default {
|
||||
openai,
|
||||
@@ -30,4 +31,5 @@ export default {
|
||||
localai,
|
||||
zhipuai,
|
||||
baichuan,
|
||||
cohere,
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ export enum ProviderEnum {
|
||||
'localai' = 'localai',
|
||||
'zhipuai' = 'zhipuai',
|
||||
'baichuan' = 'baichuan',
|
||||
'cohere' = 'cohere',
|
||||
}
|
||||
|
||||
export type ProviderConfigItem = {
|
||||
@@ -67,6 +68,7 @@ export enum ModelType {
|
||||
textGeneration = 'text-generation',
|
||||
embeddings = 'embeddings',
|
||||
speech2text = 'speech2text',
|
||||
reranking = 'reranking',
|
||||
}
|
||||
|
||||
export enum ModelFeature {
|
||||
|
||||
@@ -3,33 +3,28 @@ 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 SystemModel from './system-model'
|
||||
import config from './configs'
|
||||
import { ConfigurableProviders } from './utils'
|
||||
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 = [
|
||||
@@ -37,13 +32,6 @@ const MODEL_CARD_LIST = [
|
||||
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
|
||||
@@ -54,13 +42,8 @@ const ModelPage = () => {
|
||||
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 [showModal, setShowModal] = useState(false)
|
||||
const { notify } = useToastContext()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
@@ -76,6 +59,7 @@ const ModelPage = () => {
|
||||
config.azure_openai,
|
||||
config.replicate,
|
||||
config.huggingface_hub,
|
||||
config.cohere,
|
||||
config.zhipuai,
|
||||
config.baichuan,
|
||||
config.spark,
|
||||
@@ -91,6 +75,7 @@ const ModelPage = () => {
|
||||
else {
|
||||
modelList = [
|
||||
config.huggingface_hub,
|
||||
config.cohere,
|
||||
config.zhipuai,
|
||||
config.spark,
|
||||
config.baichuan,
|
||||
@@ -127,6 +112,7 @@ const ModelPage = () => {
|
||||
updateModelList(ModelType.textGeneration)
|
||||
updateModelList(ModelType.embeddings)
|
||||
updateModelList(ModelType.speech2text)
|
||||
updateModelList(ModelType.reranking)
|
||||
mutateProviders()
|
||||
}
|
||||
const handleSave = async (originValue?: FormValue) => {
|
||||
@@ -210,95 +196,12 @@ const ModelPage = () => {
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className='relative pt-1 -mt-2'>
|
||||
<div className='grid grid-cols-3 gap-4 mb-5'>
|
||||
<div className='w-full'>
|
||||
<div className={titleClassName}>
|
||||
{t('common.modelProvider.systemReasoningModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-reasoning-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className={tipClassName} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name }}
|
||||
modelType={ModelType.textGeneration}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className={titleClassName}>
|
||||
{t('common.modelProvider.embeddingModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-embedding-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className={tipClassName} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name }}
|
||||
modelType={ModelType.embeddings}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className={titleClassName}>
|
||||
{t('common.modelProvider.speechToTextModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-speechToText-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className={tipClassName} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name }}
|
||||
modelType={ModelType.speech2text}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center justify-between mb-2 h-8'>
|
||||
<div className='text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
|
||||
<SystemModel />
|
||||
</div>
|
||||
<div className='mb-5 h-[0.5px] bg-gray-100' />
|
||||
<div className='mb-3 text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
|
||||
<div className='grid grid-cols-2 gap-4 mb-6'>
|
||||
{
|
||||
MODEL_CARD_LIST.map((model, index) => (
|
||||
|
||||
@@ -34,6 +34,7 @@ type Props = {
|
||||
popClassName?: string
|
||||
readonly?: boolean
|
||||
triggerIconSmall?: boolean
|
||||
whenEmptyGoToSetting?: boolean
|
||||
}
|
||||
|
||||
type ModelOption = {
|
||||
@@ -57,10 +58,17 @@ const ModelSelector: FC<Props> = ({
|
||||
popClassName,
|
||||
readonly,
|
||||
triggerIconSmall,
|
||||
whenEmptyGoToSetting,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const { textGenerationModelList, embeddingsModelList, speech2textModelList, agentThoughtModelList } = useProviderContext()
|
||||
const {
|
||||
textGenerationModelList,
|
||||
embeddingsModelList,
|
||||
speech2textModelList,
|
||||
rerankModelList,
|
||||
agentThoughtModelList,
|
||||
} = useProviderContext()
|
||||
const [search, setSearch] = useState('')
|
||||
const modelList = supportAgentThought
|
||||
? agentThoughtModelList
|
||||
@@ -68,6 +76,7 @@ const ModelSelector: FC<Props> = ({
|
||||
[ModelType.textGeneration]: textGenerationModelList,
|
||||
[ModelType.embeddings]: embeddingsModelList,
|
||||
[ModelType.speech2text]: speech2textModelList,
|
||||
[ModelType.reranking]: rerankModelList,
|
||||
})[modelType]
|
||||
const currModel = modelList.find(item => item.model_name === value?.modelName && item.model_provider.provider_name === value.providerName)
|
||||
const allModelNames = (() => {
|
||||
@@ -116,7 +125,7 @@ const ModelSelector: FC<Props> = ({
|
||||
return (
|
||||
<div className=''>
|
||||
<Popover className='relative'>
|
||||
<Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
|
||||
<Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto bg-gray-100 opacity-50' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
@@ -130,7 +139,7 @@ const ModelSelector: FC<Props> = ({
|
||||
providerName={value.providerName}
|
||||
/>
|
||||
<div className='mr-1.5 grow flex items-center text-left text-sm text-gray-900 truncate'>
|
||||
<ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name} />
|
||||
<ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name || value.modelName} />
|
||||
{isShowModelModeType && (
|
||||
<ModelModeTypeLabel className='ml-2' type={currModel?.model_mode as ModelModeType} />
|
||||
)}
|
||||
@@ -237,7 +246,22 @@ const ModelSelector: FC<Props> = ({
|
||||
return null
|
||||
})
|
||||
}
|
||||
{(search && filteredModelList.length === 0) && (
|
||||
{
|
||||
whenEmptyGoToSetting && modelList.length === 0 && (
|
||||
<div className='pt-6'>
|
||||
<div className='flex items-center justify-center mx-auto mb-2 w-12 h-12 rounded-[10px] border border-[#EAECF5]'>
|
||||
<CubeOutline className='w-6 h-6 text-gray-500' />
|
||||
</div>
|
||||
<div className='mb-1 text-center text-[13px] font-medium text-gray-500'>
|
||||
{t('common.modelProvider.selector.emptyTip')}
|
||||
</div>
|
||||
<div className='mb-6 text-center text-xs text-primary-500'>
|
||||
<span onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('common.modelProvider.selector.emptySetting')}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{modelList.length !== 0 && (search && filteredModelList.length === 0) && (
|
||||
<div className='px-3 pt-1.5 h-[30px] text-center text-xs text-gray-500'>{t('common.modelProvider.noModelFound', { model: search })}</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ModelSelector from '../model-selector'
|
||||
import type {
|
||||
BackendModel, ProviderEnum,
|
||||
} from '../declarations'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { HelpCircle, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { updateDefaultModel } from '@/service/common'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
const SystemModel = () => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
textGenerationDefaultModel,
|
||||
mutateTextGenerationDefaultModel,
|
||||
embeddingsDefaultModel,
|
||||
mutateEmbeddingsDefaultModel,
|
||||
speech2textDefaultModel,
|
||||
mutateSpeech2textDefaultModel,
|
||||
rerankDefaultModel,
|
||||
mutateRerankDefaultModel,
|
||||
} = useProviderContext()
|
||||
const { notify } = useToastContext()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [selectedModel, setSelectedModel] = useState<Record<ModelType, { providerName: ProviderEnum; modelName: string } | undefined>>({
|
||||
[ModelType.textGeneration]: textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name },
|
||||
[ModelType.embeddings]: embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name },
|
||||
[ModelType.speech2text]: speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name },
|
||||
[ModelType.reranking]: rerankDefaultModel && { providerName: rerankDefaultModel.model_provider.provider_name, modelName: rerankDefaultModel.model_name },
|
||||
})
|
||||
|
||||
const mutateDefaultModel = (types: ModelType[]) => {
|
||||
types.forEach((type) => {
|
||||
if (type === ModelType.textGeneration)
|
||||
mutateTextGenerationDefaultModel()
|
||||
if (type === ModelType.embeddings)
|
||||
mutateEmbeddingsDefaultModel()
|
||||
if (type === ModelType.speech2text)
|
||||
mutateSpeech2textDefaultModel()
|
||||
if (type === ModelType.reranking)
|
||||
mutateRerankDefaultModel()
|
||||
})
|
||||
}
|
||||
const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => {
|
||||
setSelectedModel({
|
||||
...selectedModel,
|
||||
[type]: {
|
||||
providerName: v.model_provider.provider_name,
|
||||
modelName: v.model_name,
|
||||
},
|
||||
})
|
||||
}
|
||||
const handleSave = async () => {
|
||||
const kesArray = Object.keys(selectedModel) as ModelType[]
|
||||
const res = await updateDefaultModel({
|
||||
url: '/workspaces/current/default-model',
|
||||
body: {
|
||||
model_settings: kesArray.map((key) => {
|
||||
return {
|
||||
model_type: key,
|
||||
provider_name: selectedModel?.[key]?.providerName,
|
||||
model_name: selectedModel?.[key]?.modelName,
|
||||
}
|
||||
}),
|
||||
},
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
mutateDefaultModel(kesArray)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-end'
|
||||
offset={{
|
||||
mainAxis: 4,
|
||||
crossAxis: 8,
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
|
||||
<div className={`
|
||||
flex items-center px-2 h-6 text-xs text-gray-700 cursor-pointer rounded-md border-[0.5px] border-gray-200 shadow-xs
|
||||
hover:bg-gray-100 hover:shadow-none
|
||||
${open && 'bg-gray-100 shadow-none'}
|
||||
`}>
|
||||
<Settings01 className='mr-1 w-3 h-3 text-gray-500' />
|
||||
{t('common.modelProvider.systemModelSettings')}
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-50'>
|
||||
<div className='pt-4 w-[360px] rounded-xl border-[0.5px] border-black/5 bg-white shadow-xl'>
|
||||
<div className='px-6 py-1'>
|
||||
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
|
||||
{t('common.modelProvider.systemReasoningModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-reasoning-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={selectedModel[ModelType.textGeneration]}
|
||||
modelType={ModelType.textGeneration}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='px-6 py-1'>
|
||||
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
|
||||
{t('common.modelProvider.embeddingModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-embedding-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={selectedModel[ModelType.embeddings]}
|
||||
modelType={ModelType.embeddings}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='px-6 py-1'>
|
||||
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
|
||||
{t('common.modelProvider.rerankModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-rerankModel-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.rerankModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={selectedModel[ModelType.reranking]}
|
||||
modelType={ModelType.reranking}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.reranking, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='px-6 py-1'>
|
||||
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
|
||||
{t('common.modelProvider.speechToTextModel.key')}
|
||||
<Tooltip
|
||||
selector='model-page-system-speechToText-model-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
|
||||
}
|
||||
>
|
||||
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
value={selectedModel[ModelType.speech2text]}
|
||||
modelType={ModelType.speech2text}
|
||||
onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center justify-end px-6 py-4'>
|
||||
<Button
|
||||
className='mr-2 !h-8 !text-[13px]'
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
className='!h-8 !text-[13px]'
|
||||
onClick={handleSave}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default SystemModel
|
||||
Reference in New Issue
Block a user