Refactor/model credential (#24994)
This commit is contained in:
parent
b88146c443
commit
9e125e2029
30 changed files with 1226 additions and 596 deletions
|
|
@ -5,6 +5,7 @@ import {
|
|||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -15,6 +16,7 @@ import type {
|
|||
import {
|
||||
ConfigurationMethodEnum,
|
||||
FormTypeEnum,
|
||||
ModelModalModeEnum,
|
||||
} from '../declarations'
|
||||
import {
|
||||
useLanguage,
|
||||
|
|
@ -46,16 +48,19 @@ import {
|
|||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import { CredentialSelector } from '../model-auth'
|
||||
|
||||
type ModelModalProps = {
|
||||
provider: ModelProvider
|
||||
configurateMethod: ConfigurationMethodEnum
|
||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||
onCancel: () => void
|
||||
onSave: () => void
|
||||
onSave: (formValues?: Record<string, any>) => void
|
||||
onRemove: (formValues?: Record<string, any>) => void
|
||||
model?: CustomModel
|
||||
credential?: Credential
|
||||
isModelCredential?: boolean
|
||||
mode?: ModelModalModeEnum
|
||||
}
|
||||
|
||||
const ModelModal: FC<ModelModalProps> = ({
|
||||
|
|
@ -67,6 +72,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
model,
|
||||
credential,
|
||||
isModelCredential,
|
||||
mode = ModelModalModeEnum.configProviderCredential,
|
||||
}) => {
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
||||
|
|
@ -81,40 +87,88 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
closeConfirmDelete,
|
||||
openConfirmDelete,
|
||||
doingAction,
|
||||
} = useAuth(provider, configurateMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onSave)
|
||||
handleActiveCredential,
|
||||
} = useAuth(
|
||||
provider,
|
||||
configurateMethod,
|
||||
currentCustomConfigurationModelFixedFields,
|
||||
{
|
||||
isModelCredential,
|
||||
mode,
|
||||
},
|
||||
)
|
||||
const {
|
||||
credentials: formSchemasValue,
|
||||
available_credentials,
|
||||
} = credentialData as any
|
||||
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
const {
|
||||
formSchemas,
|
||||
formValues,
|
||||
modelNameAndTypeFormSchemas,
|
||||
modelNameAndTypeFormValues,
|
||||
} = useModelFormSchemas(provider, providerFormSchemaPredefined, formSchemasValue, credential, model)
|
||||
const formRef = useRef<FormRefObject>(null)
|
||||
const formRef1 = useRef<FormRefObject>(null)
|
||||
const [selectedCredential, setSelectedCredential] = useState<Credential & { addNewCredential?: boolean } | undefined>()
|
||||
const formRef2 = useRef<FormRefObject>(null)
|
||||
const isEditMode = !!Object.keys(formValues).filter((key) => {
|
||||
return key !== '__model_name' && key !== '__model_type'
|
||||
}).length && isCurrentWorkspaceManager
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (mode === ModelModalModeEnum.addCustomModelToModelList && selectedCredential && !selectedCredential?.addNewCredential) {
|
||||
handleActiveCredential(selectedCredential, model)
|
||||
onCancel()
|
||||
return
|
||||
}
|
||||
|
||||
let modelNameAndTypeIsCheckValidated = true
|
||||
let modelNameAndTypeValues: Record<string, any> = {}
|
||||
|
||||
if (mode === ModelModalModeEnum.configCustomModel) {
|
||||
const formResult = formRef1.current?.getFormValues({
|
||||
needCheckValidatedValues: true,
|
||||
}) || { isCheckValidated: false, values: {} }
|
||||
modelNameAndTypeIsCheckValidated = formResult.isCheckValidated
|
||||
modelNameAndTypeValues = formResult.values
|
||||
}
|
||||
|
||||
if (mode === ModelModalModeEnum.configModelCredential && model) {
|
||||
modelNameAndTypeValues = {
|
||||
__model_name: model.model,
|
||||
__model_type: model.model_type,
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === ModelModalModeEnum.addCustomModelToModelList && selectedCredential?.addNewCredential && model) {
|
||||
modelNameAndTypeValues = {
|
||||
__model_name: model.model,
|
||||
__model_type: model.model_type,
|
||||
}
|
||||
}
|
||||
const {
|
||||
isCheckValidated,
|
||||
values,
|
||||
} = formRef.current?.getFormValues({
|
||||
} = formRef2.current?.getFormValues({
|
||||
needCheckValidatedValues: true,
|
||||
needTransformWhenSecretFieldIsPristine: true,
|
||||
}) || { isCheckValidated: false, values: {} }
|
||||
if (!isCheckValidated)
|
||||
if (!isCheckValidated || !modelNameAndTypeIsCheckValidated)
|
||||
return
|
||||
|
||||
const {
|
||||
__authorization_name__,
|
||||
__model_name,
|
||||
__model_type,
|
||||
} = modelNameAndTypeValues
|
||||
const {
|
||||
__authorization_name__,
|
||||
...rest
|
||||
} = values
|
||||
if (__model_name && __model_type) {
|
||||
handleSaveCredential({
|
||||
if (__model_name && __model_type && __authorization_name__) {
|
||||
await handleSaveCredential({
|
||||
credential_id: credential?.credential_id,
|
||||
credentials: rest,
|
||||
name: __authorization_name__,
|
||||
|
|
@ -123,41 +177,33 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
})
|
||||
}
|
||||
else {
|
||||
handleSaveCredential({
|
||||
await handleSaveCredential({
|
||||
credential_id: credential?.credential_id,
|
||||
credentials: rest,
|
||||
name: __authorization_name__,
|
||||
})
|
||||
}
|
||||
}, [handleSaveCredential, credential?.credential_id, model])
|
||||
onSave(values)
|
||||
}, [handleSaveCredential, credential?.credential_id, model, onSave, mode, selectedCredential, handleActiveCredential])
|
||||
|
||||
const modalTitle = useMemo(() => {
|
||||
if (!providerFormSchemaPredefined && !model) {
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<ModelIcon
|
||||
className='mr-2 h-10 w-10 shrink-0'
|
||||
iconClassName='h-10 w-10'
|
||||
provider={provider}
|
||||
/>
|
||||
<div>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>{t('common.modelProvider.auth.apiKeyModal.addModel')}</div>
|
||||
<div className='system-md-semibold text-text-primary'>{renderI18nObject(provider.label)}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
let label = t('common.modelProvider.auth.apiKeyModal.title')
|
||||
|
||||
if (model)
|
||||
label = t('common.modelProvider.auth.addModelCredential')
|
||||
if (mode === ModelModalModeEnum.configCustomModel || mode === ModelModalModeEnum.addCustomModelToModelList)
|
||||
label = t('common.modelProvider.auth.addModel')
|
||||
if (mode === ModelModalModeEnum.configModelCredential) {
|
||||
if (credential)
|
||||
label = t('common.modelProvider.auth.editModelCredential')
|
||||
else
|
||||
label = t('common.modelProvider.auth.addModelCredential')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='title-2xl-semi-bold text-text-primary'>
|
||||
{label}
|
||||
</div>
|
||||
)
|
||||
}, [providerFormSchemaPredefined, t, model, renderI18nObject])
|
||||
}, [t, mode, credential])
|
||||
|
||||
const modalDesc = useMemo(() => {
|
||||
if (providerFormSchemaPredefined) {
|
||||
|
|
@ -172,7 +218,18 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
}, [providerFormSchemaPredefined, t])
|
||||
|
||||
const modalModel = useMemo(() => {
|
||||
if (model) {
|
||||
if (mode === ModelModalModeEnum.configCustomModel) {
|
||||
return (
|
||||
<div className='mt-2 flex items-center'>
|
||||
<ModelIcon
|
||||
className='mr-2 h-4 w-4 shrink-0'
|
||||
provider={provider}
|
||||
/>
|
||||
<div className='system-md-regular mr-1 text-text-secondary'>{renderI18nObject(provider.label)}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (model && (mode === ModelModalModeEnum.configModelCredential || mode === ModelModalModeEnum.addCustomModelToModelList)) {
|
||||
return (
|
||||
<div className='mt-2 flex items-center'>
|
||||
<ModelIcon
|
||||
|
|
@ -187,7 +244,38 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
}
|
||||
|
||||
return null
|
||||
}, [model, provider])
|
||||
}, [model, provider, mode, renderI18nObject])
|
||||
|
||||
const showCredentialLabel = useMemo(() => {
|
||||
if (mode === ModelModalModeEnum.configCustomModel)
|
||||
return true
|
||||
if (mode === ModelModalModeEnum.addCustomModelToModelList)
|
||||
return selectedCredential?.addNewCredential
|
||||
}, [mode, selectedCredential])
|
||||
const showCredentialForm = useMemo(() => {
|
||||
if (mode !== ModelModalModeEnum.addCustomModelToModelList)
|
||||
return true
|
||||
return selectedCredential?.addNewCredential
|
||||
}, [mode, selectedCredential])
|
||||
const saveButtonText = useMemo(() => {
|
||||
if (mode === ModelModalModeEnum.addCustomModelToModelList || mode === ModelModalModeEnum.configCustomModel)
|
||||
return t('common.operation.add')
|
||||
return t('common.operation.save')
|
||||
}, [mode, t])
|
||||
|
||||
const handleDeleteCredential = useCallback(() => {
|
||||
handleConfirmDelete()
|
||||
onCancel()
|
||||
}, [handleConfirmDelete])
|
||||
|
||||
const handleModelNameAndTypeChange = useCallback((field: string, value: any) => {
|
||||
const {
|
||||
getForm,
|
||||
} = formRef2.current as FormRefObject || {}
|
||||
if (getForm())
|
||||
getForm()?.setFieldValue(field, value)
|
||||
}, [])
|
||||
const notAllowCustomCredential = provider.allow_custom_token === false
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
|
|
@ -214,100 +302,132 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
>
|
||||
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='px-6 pt-6'>
|
||||
<div className='pb-3'>
|
||||
{modalTitle}
|
||||
{modalDesc}
|
||||
{modalModel}
|
||||
</div>
|
||||
|
||||
<div className='max-h-[calc(100vh-320px)] overflow-y-auto'>
|
||||
{
|
||||
isLoading && (
|
||||
<div className='flex items-center justify-center'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!isLoading && (
|
||||
<AuthForm
|
||||
formSchemas={formSchemas.map((formSchema) => {
|
||||
return {
|
||||
...formSchema,
|
||||
name: formSchema.variable,
|
||||
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
||||
}
|
||||
}) as FormSchema[]}
|
||||
defaultValues={formValues}
|
||||
inputClassName='justify-start'
|
||||
ref={formRef}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='sticky bottom-0 -mx-2 mt-2 flex flex-wrap items-center justify-between gap-y-2 bg-components-panel-bg px-2 pb-6 pt-4'>
|
||||
{
|
||||
(provider.help && (provider.help.title || provider.help.url))
|
||||
? (
|
||||
<a
|
||||
href={provider.help?.url[language] || provider.help?.url.en_US}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='inline-flex items-center text-xs text-primary-600'
|
||||
onClick={e => !provider.help.url && e.preventDefault()}
|
||||
>
|
||||
{provider.help.title?.[language] || provider.help.url[language] || provider.help.title?.en_US || provider.help.url.en_US}
|
||||
<LinkExternal02 className='ml-1 h-3 w-3' />
|
||||
</a>
|
||||
)
|
||||
: <div />
|
||||
}
|
||||
<div>
|
||||
{
|
||||
isEditMode && (
|
||||
<Button
|
||||
variant='warning'
|
||||
size='large'
|
||||
className='mr-2'
|
||||
onClick={() => openConfirmDelete(credential, model)}
|
||||
>
|
||||
{t('common.operation.remove')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<Button
|
||||
size='large'
|
||||
className='mr-2'
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
size='large'
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
disabled={isLoading || doingAction}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-6 pb-3'>
|
||||
{modalTitle}
|
||||
{modalDesc}
|
||||
{modalModel}
|
||||
</div>
|
||||
<div className='border-t-[0.5px] border-t-divider-regular'>
|
||||
<div className='flex items-center justify-center rounded-b-2xl bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
||||
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
||||
{t('common.modelProvider.encrypted.front')}
|
||||
<a
|
||||
className='mx-1 text-text-accent'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
||||
<div className='max-h-[calc(100vh-320px)] overflow-y-auto px-6 py-3'>
|
||||
{
|
||||
mode === ModelModalModeEnum.configCustomModel && (
|
||||
<AuthForm
|
||||
formSchemas={modelNameAndTypeFormSchemas.map((formSchema) => {
|
||||
return {
|
||||
...formSchema,
|
||||
name: formSchema.variable,
|
||||
}
|
||||
}) as FormSchema[]}
|
||||
defaultValues={modelNameAndTypeFormValues}
|
||||
inputClassName='justify-start'
|
||||
ref={formRef1}
|
||||
onChange={handleModelNameAndTypeChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
mode === ModelModalModeEnum.addCustomModelToModelList && (
|
||||
<CredentialSelector
|
||||
credentials={available_credentials || []}
|
||||
onSelect={setSelectedCredential}
|
||||
selectedCredential={selectedCredential}
|
||||
disabled={isLoading}
|
||||
notAllowAddNewCredential={notAllowCustomCredential}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
showCredentialLabel && (
|
||||
<div className='system-xs-medium-uppercase mb-3 mt-6 flex items-center text-text-tertiary'>
|
||||
{t('common.modelProvider.auth.modelCredential')}
|
||||
<div className='ml-2 h-px grow bg-gradient-to-r from-divider-regular to-background-gradient-mask-transparent' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
isLoading && (
|
||||
<div className='mt-3 flex items-center justify-center'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!isLoading
|
||||
&& showCredentialForm
|
||||
&& (
|
||||
<AuthForm
|
||||
formSchemas={formSchemas.map((formSchema) => {
|
||||
return {
|
||||
...formSchema,
|
||||
name: formSchema.variable,
|
||||
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
||||
}
|
||||
}) as FormSchema[]}
|
||||
defaultValues={formValues}
|
||||
inputClassName='justify-start'
|
||||
ref={formRef2}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='flex justify-between p-6 pt-5'>
|
||||
{
|
||||
(provider.help && (provider.help.title || provider.help.url))
|
||||
? (
|
||||
<a
|
||||
href={provider.help?.url[language] || provider.help?.url.en_US}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='system-xs-regular mt-2 inline-flex items-center text-text-accent'
|
||||
onClick={e => !provider.help.url && e.preventDefault()}
|
||||
>
|
||||
{provider.help.title?.[language] || provider.help.url[language] || provider.help.title?.en_US || provider.help.url.en_US}
|
||||
<LinkExternal02 className='ml-1 h-3 w-3' />
|
||||
</a>
|
||||
)
|
||||
: <div />
|
||||
}
|
||||
<div className='flex items-center justify-end space-x-2'>
|
||||
{
|
||||
isEditMode && (
|
||||
<Button
|
||||
variant='warning'
|
||||
onClick={() => openConfirmDelete(credential, model)}
|
||||
>
|
||||
{t('common.operation.remove')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<Button
|
||||
onClick={onCancel}
|
||||
>
|
||||
PKCS1_OAEP
|
||||
</a>
|
||||
{t('common.modelProvider.encrypted.back')}
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
disabled={isLoading || doingAction}
|
||||
>
|
||||
{saveButtonText}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
(mode === ModelModalModeEnum.configCustomModel || mode === ModelModalModeEnum.configProviderCredential) && (
|
||||
<div className='border-t-[0.5px] border-t-divider-regular'>
|
||||
<div className='flex items-center justify-center rounded-b-2xl bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
||||
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
||||
{t('common.modelProvider.encrypted.front')}
|
||||
<a
|
||||
className='mx-1 text-text-accent'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
||||
>
|
||||
PKCS1_OAEP
|
||||
</a>
|
||||
{t('common.modelProvider.encrypted.back')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
deleteCredentialId && (
|
||||
|
|
@ -316,7 +436,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||
title={t('common.modelProvider.confirmDelete')}
|
||||
isDisabled={doingAction}
|
||||
onCancel={closeConfirmDelete}
|
||||
onConfirm={handleConfirmDelete}
|
||||
onConfirm={handleDeleteCredential}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue