Refactor/model credential (#24994)
This commit is contained in:
parent
b88146c443
commit
9e125e2029
30 changed files with 1226 additions and 596 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
isValidElement,
|
isValidElement,
|
||||||
memo,
|
memo,
|
||||||
|
useCallback,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { RiExternalLinkLine } from '@remixicon/react'
|
import { RiExternalLinkLine } from '@remixicon/react'
|
||||||
|
|
@ -23,6 +24,7 @@ export type BaseFieldProps = {
|
||||||
formSchema: FormSchema
|
formSchema: FormSchema
|
||||||
field: AnyFieldApi
|
field: AnyFieldApi
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
onChange?: (field: string, value: any) => void
|
||||||
}
|
}
|
||||||
const BaseField = ({
|
const BaseField = ({
|
||||||
fieldClassName,
|
fieldClassName,
|
||||||
|
|
@ -32,6 +34,7 @@ const BaseField = ({
|
||||||
formSchema,
|
formSchema,
|
||||||
field,
|
field,
|
||||||
disabled: propsDisabled,
|
disabled: propsDisabled,
|
||||||
|
onChange,
|
||||||
}: BaseFieldProps) => {
|
}: BaseFieldProps) => {
|
||||||
const renderI18nObject = useRenderI18nObject()
|
const renderI18nObject = useRenderI18nObject()
|
||||||
const {
|
const {
|
||||||
|
|
@ -40,7 +43,6 @@ const BaseField = ({
|
||||||
placeholder,
|
placeholder,
|
||||||
options,
|
options,
|
||||||
labelClassName: formLabelClassName,
|
labelClassName: formLabelClassName,
|
||||||
show_on = [],
|
|
||||||
disabled: formSchemaDisabled,
|
disabled: formSchemaDisabled,
|
||||||
} = formSchema
|
} = formSchema
|
||||||
const disabled = propsDisabled || formSchemaDisabled
|
const disabled = propsDisabled || formSchemaDisabled
|
||||||
|
|
@ -90,21 +92,11 @@ const BaseField = ({
|
||||||
}) || []
|
}) || []
|
||||||
}, [options, renderI18nObject, optionValues])
|
}, [options, renderI18nObject, optionValues])
|
||||||
const value = useStore(field.form.store, s => s.values[field.name])
|
const value = useStore(field.form.store, s => s.values[field.name])
|
||||||
const values = useStore(field.form.store, (s) => {
|
|
||||||
return show_on.reduce((acc, condition) => {
|
|
||||||
acc[condition.variable] = s.values[condition.variable]
|
|
||||||
return acc
|
|
||||||
}, {} as Record<string, any>)
|
|
||||||
})
|
|
||||||
const show = useMemo(() => {
|
|
||||||
return show_on.every((condition) => {
|
|
||||||
const conditionValue = values[condition.variable]
|
|
||||||
return conditionValue === condition.value
|
|
||||||
})
|
|
||||||
}, [values, show_on])
|
|
||||||
|
|
||||||
if (!show)
|
const handleChange = useCallback((value: any) => {
|
||||||
return null
|
field.handleChange(value)
|
||||||
|
onChange?.(field.name, value)
|
||||||
|
}, [field, onChange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(fieldClassName)}>
|
<div className={cn(fieldClassName)}>
|
||||||
|
|
@ -124,7 +116,9 @@ const BaseField = ({
|
||||||
name={field.name}
|
name={field.name}
|
||||||
className={cn(inputClassName)}
|
className={cn(inputClassName)}
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={e => field.handleChange(e.target.value)}
|
onChange={(e) => {
|
||||||
|
handleChange(e.target.value)
|
||||||
|
}}
|
||||||
onBlur={field.handleBlur}
|
onBlur={field.handleBlur}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={memorizedPlaceholder}
|
placeholder={memorizedPlaceholder}
|
||||||
|
|
@ -139,7 +133,7 @@ const BaseField = ({
|
||||||
type='password'
|
type='password'
|
||||||
className={cn(inputClassName)}
|
className={cn(inputClassName)}
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={e => field.handleChange(e.target.value)}
|
onChange={e => handleChange(e.target.value)}
|
||||||
onBlur={field.handleBlur}
|
onBlur={field.handleBlur}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={memorizedPlaceholder}
|
placeholder={memorizedPlaceholder}
|
||||||
|
|
@ -155,7 +149,7 @@ const BaseField = ({
|
||||||
type='number'
|
type='number'
|
||||||
className={cn(inputClassName)}
|
className={cn(inputClassName)}
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={e => field.handleChange(e.target.value)}
|
onChange={e => handleChange(e.target.value)}
|
||||||
onBlur={field.handleBlur}
|
onBlur={field.handleBlur}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={memorizedPlaceholder}
|
placeholder={memorizedPlaceholder}
|
||||||
|
|
@ -166,11 +160,14 @@ const BaseField = ({
|
||||||
formSchema.type === FormTypeEnum.select && (
|
formSchema.type === FormTypeEnum.select && (
|
||||||
<PureSelect
|
<PureSelect
|
||||||
value={value}
|
value={value}
|
||||||
onChange={v => field.handleChange(v)}
|
onChange={v => handleChange(v)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder={memorizedPlaceholder}
|
placeholder={memorizedPlaceholder}
|
||||||
options={memorizedOptions}
|
options={memorizedOptions}
|
||||||
triggerPopupSameWidth
|
triggerPopupSameWidth
|
||||||
|
popupProps={{
|
||||||
|
className: 'max-h-[320px] overflow-y-auto',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +186,7 @@ const BaseField = ({
|
||||||
disabled && 'cursor-not-allowed opacity-50',
|
disabled && 'cursor-not-allowed opacity-50',
|
||||||
inputClassName,
|
inputClassName,
|
||||||
)}
|
)}
|
||||||
onClick={() => !disabled && field.handleChange(option.value)}
|
onClick={() => !disabled && handleChange(option.value)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
formSchema.showRadioUI && (
|
formSchema.showRadioUI && (
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ import type {
|
||||||
AnyFieldApi,
|
AnyFieldApi,
|
||||||
AnyFormApi,
|
AnyFormApi,
|
||||||
} from '@tanstack/react-form'
|
} from '@tanstack/react-form'
|
||||||
import { useForm } from '@tanstack/react-form'
|
import {
|
||||||
|
useForm,
|
||||||
|
useStore,
|
||||||
|
} from '@tanstack/react-form'
|
||||||
import type {
|
import type {
|
||||||
FormRef,
|
FormRef,
|
||||||
FormSchema,
|
FormSchema,
|
||||||
|
|
@ -32,6 +35,7 @@ export type BaseFormProps = {
|
||||||
ref?: FormRef
|
ref?: FormRef
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
formFromProps?: AnyFormApi
|
formFromProps?: AnyFormApi
|
||||||
|
onChange?: (field: string, value: any) => void
|
||||||
} & Pick<BaseFieldProps, 'fieldClassName' | 'labelClassName' | 'inputContainerClassName' | 'inputClassName'>
|
} & Pick<BaseFieldProps, 'fieldClassName' | 'labelClassName' | 'inputContainerClassName' | 'inputClassName'>
|
||||||
|
|
||||||
const BaseForm = ({
|
const BaseForm = ({
|
||||||
|
|
@ -45,6 +49,7 @@ const BaseForm = ({
|
||||||
ref,
|
ref,
|
||||||
disabled,
|
disabled,
|
||||||
formFromProps,
|
formFromProps,
|
||||||
|
onChange,
|
||||||
}: BaseFormProps) => {
|
}: BaseFormProps) => {
|
||||||
const initialDefaultValues = useMemo(() => {
|
const initialDefaultValues = useMemo(() => {
|
||||||
if (defaultValues)
|
if (defaultValues)
|
||||||
|
|
@ -63,6 +68,19 @@ const BaseForm = ({
|
||||||
const { getFormValues } = useGetFormValues(form, formSchemas)
|
const { getFormValues } = useGetFormValues(form, formSchemas)
|
||||||
const { getValidators } = useGetValidators()
|
const { getValidators } = useGetValidators()
|
||||||
|
|
||||||
|
const showOnValues = useStore(form.store, (s: any) => {
|
||||||
|
const result: Record<string, any> = {}
|
||||||
|
formSchemas.forEach((schema) => {
|
||||||
|
const { show_on } = schema
|
||||||
|
if (show_on?.length) {
|
||||||
|
show_on.forEach((condition) => {
|
||||||
|
result[condition.variable] = s.values[condition.variable]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
useImperativeHandle(ref, () => {
|
useImperativeHandle(ref, () => {
|
||||||
return {
|
return {
|
||||||
getForm() {
|
getForm() {
|
||||||
|
|
@ -87,19 +105,29 @@ const BaseForm = ({
|
||||||
inputContainerClassName={inputContainerClassName}
|
inputContainerClassName={inputContainerClassName}
|
||||||
inputClassName={inputClassName}
|
inputClassName={inputClassName}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled])
|
}, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange])
|
||||||
|
|
||||||
const renderFieldWrapper = useCallback((formSchema: FormSchema) => {
|
const renderFieldWrapper = useCallback((formSchema: FormSchema) => {
|
||||||
const validators = getValidators(formSchema)
|
const validators = getValidators(formSchema)
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
|
show_on = [],
|
||||||
} = formSchema
|
} = formSchema
|
||||||
|
|
||||||
|
const show = show_on?.every((condition) => {
|
||||||
|
const conditionValue = showOnValues[condition.variable]
|
||||||
|
return conditionValue === condition.value
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!show)
|
||||||
|
return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form.Field
|
<form.Field
|
||||||
key={name}
|
key={name}
|
||||||
|
|
@ -109,7 +137,7 @@ const BaseForm = ({
|
||||||
{renderField}
|
{renderField}
|
||||||
</form.Field>
|
</form.Field>
|
||||||
)
|
)
|
||||||
}, [renderField, form, getValidators])
|
}, [renderField, form, getValidators, showOnValues])
|
||||||
|
|
||||||
if (!formSchemas?.length)
|
if (!formSchemas?.length)
|
||||||
return null
|
return null
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@ export type CustomModelCredential = CustomModel & {
|
||||||
credentials?: Record<string, any>
|
credentials?: Record<string, any>
|
||||||
available_model_credentials?: Credential[]
|
available_model_credentials?: Credential[]
|
||||||
current_credential_id?: string
|
current_credential_id?: string
|
||||||
|
current_credential_name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CredentialWithModel = Credential & {
|
export type CredentialWithModel = Credential & {
|
||||||
|
|
@ -236,6 +237,10 @@ export type ModelProvider = {
|
||||||
current_credential_name?: string
|
current_credential_name?: string
|
||||||
available_credentials?: Credential[]
|
available_credentials?: Credential[]
|
||||||
custom_models?: CustomModelCredential[]
|
custom_models?: CustomModelCredential[]
|
||||||
|
can_added_models?: {
|
||||||
|
model: string
|
||||||
|
model_type: ModelTypeEnum
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
system_configuration: {
|
system_configuration: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
|
@ -323,3 +328,10 @@ export type ModelCredential = {
|
||||||
current_credential_id?: string
|
current_credential_id?: string
|
||||||
current_credential_name?: string
|
current_credential_name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ModelModalModeEnum {
|
||||||
|
configProviderCredential = 'config-provider-credential',
|
||||||
|
configCustomModel = 'config-custom-model',
|
||||||
|
addCustomModelToModelList = 'add-custom-model-to-model-list',
|
||||||
|
configModelCredential = 'config-model-credential',
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import type {
|
||||||
DefaultModel,
|
DefaultModel,
|
||||||
DefaultModelResponse,
|
DefaultModelResponse,
|
||||||
Model,
|
Model,
|
||||||
|
ModelModalModeEnum,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
ModelTypeEnum,
|
ModelTypeEnum,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
|
|
@ -348,29 +349,31 @@ export const useRefreshModel = () => {
|
||||||
|
|
||||||
export const useModelModalHandler = () => {
|
export const useModelModalHandler = () => {
|
||||||
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||||
const { handleRefreshModel } = useRefreshModel()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
isModelCredential?: boolean,
|
extra: {
|
||||||
credential?: Credential,
|
isModelCredential?: boolean,
|
||||||
model?: CustomModel,
|
credential?: Credential,
|
||||||
onUpdate?: () => void,
|
model?: CustomModel,
|
||||||
|
onUpdate?: (newPayload: any, formValues?: Record<string, any>) => void,
|
||||||
|
mode?: ModelModalModeEnum,
|
||||||
|
} = {},
|
||||||
) => {
|
) => {
|
||||||
setShowModelModal({
|
setShowModelModal({
|
||||||
payload: {
|
payload: {
|
||||||
currentProvider: provider,
|
currentProvider: provider,
|
||||||
currentConfigurationMethod: configurationMethod,
|
currentConfigurationMethod: configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||||
isModelCredential,
|
isModelCredential: extra.isModelCredential,
|
||||||
credential,
|
credential: extra.credential,
|
||||||
model,
|
model: extra.model,
|
||||||
|
mode: extra.mode,
|
||||||
},
|
},
|
||||||
onSaveCallback: () => {
|
onSaveCallback: (newPayload, formValues) => {
|
||||||
handleRefreshModel(provider, configurationMethod, CustomConfigurationModelFixedFields)
|
extra.onUpdate?.(newPayload, formValues)
|
||||||
onUpdate?.()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { RiAddLine } from '@remixicon/react'
|
import { RiAddLine } from '@remixicon/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
@ -9,20 +8,22 @@ import { Authorized } from '@/app/components/header/account-setting/model-provid
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import type {
|
import type {
|
||||||
Credential,
|
Credential,
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
CustomModelCredential,
|
CustomModelCredential,
|
||||||
ModelCredential,
|
ModelCredential,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { ConfigurationMethodEnum, ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
|
||||||
|
|
||||||
type AddCredentialInLoadBalancingProps = {
|
type AddCredentialInLoadBalancingProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
model: CustomModelCredential
|
model: CustomModelCredential
|
||||||
configurationMethod: ConfigurationMethodEnum
|
configurationMethod: ConfigurationMethodEnum
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
modelCredential: ModelCredential
|
modelCredential: ModelCredential
|
||||||
onSelectCredential: (credential: Credential) => void
|
onSelectCredential: (credential: Credential) => void
|
||||||
onUpdate?: () => void
|
onUpdate?: (payload?: any, formValues?: Record<string, any>) => void
|
||||||
|
onRemove?: (credentialId: string) => void
|
||||||
}
|
}
|
||||||
const AddCredentialInLoadBalancing = ({
|
const AddCredentialInLoadBalancing = ({
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -31,41 +32,17 @@ const AddCredentialInLoadBalancing = ({
|
||||||
modelCredential,
|
modelCredential,
|
||||||
onSelectCredential,
|
onSelectCredential,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
}: AddCredentialInLoadBalancingProps) => {
|
}: AddCredentialInLoadBalancingProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
available_credentials,
|
available_credentials,
|
||||||
} = modelCredential
|
} = modelCredential
|
||||||
const customModel = configurationMethod === ConfigurationMethodEnum.customizableModel
|
const isCustomModel = configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||||
const notAllowCustomCredential = provider.allow_custom_token === false
|
const notAllowCustomCredential = provider.allow_custom_token === false
|
||||||
|
const handleUpdate = useCallback((payload?: any, formValues?: Record<string, any>) => {
|
||||||
const ButtonComponent = useMemo(() => {
|
onUpdate?.(payload, formValues)
|
||||||
const Item = (
|
}, [onUpdate])
|
||||||
<div className={cn(
|
|
||||||
'system-sm-medium flex h-8 items-center rounded-lg px-3 text-text-accent hover:bg-state-base-hover',
|
|
||||||
notAllowCustomCredential && 'cursor-not-allowed opacity-50',
|
|
||||||
)}>
|
|
||||||
<RiAddLine className='mr-2 h-4 w-4' />
|
|
||||||
{
|
|
||||||
customModel
|
|
||||||
? t('common.modelProvider.auth.addCredential')
|
|
||||||
: t('common.modelProvider.auth.addApiKey')
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (notAllowCustomCredential) {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
asChild
|
|
||||||
popupContent={t('plugin.auth.credentialUnavailable')}
|
|
||||||
>
|
|
||||||
{Item}
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return Item
|
|
||||||
}, [notAllowCustomCredential, t, customModel])
|
|
||||||
|
|
||||||
const renderTrigger = useCallback((open?: boolean) => {
|
const renderTrigger = useCallback((open?: boolean) => {
|
||||||
const Item = (
|
const Item = (
|
||||||
|
|
@ -74,40 +51,40 @@ const AddCredentialInLoadBalancing = ({
|
||||||
open && 'bg-state-base-hover',
|
open && 'bg-state-base-hover',
|
||||||
)}>
|
)}>
|
||||||
<RiAddLine className='mr-2 h-4 w-4' />
|
<RiAddLine className='mr-2 h-4 w-4' />
|
||||||
{
|
{t('common.modelProvider.auth.addCredential')}
|
||||||
customModel
|
|
||||||
? t('common.modelProvider.auth.addCredential')
|
|
||||||
: t('common.modelProvider.auth.addApiKey')
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
return Item
|
return Item
|
||||||
}, [t, customModel])
|
}, [t, isCustomModel])
|
||||||
|
|
||||||
if (!available_credentials?.length)
|
|
||||||
return ButtonComponent
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Authorized
|
<Authorized
|
||||||
provider={provider}
|
provider={provider}
|
||||||
renderTrigger={renderTrigger}
|
renderTrigger={renderTrigger}
|
||||||
|
authParams={{
|
||||||
|
isModelCredential: isCustomModel,
|
||||||
|
mode: ModelModalModeEnum.configModelCredential,
|
||||||
|
onUpdate: handleUpdate,
|
||||||
|
onRemove,
|
||||||
|
}}
|
||||||
|
triggerOnlyOpenModal={!available_credentials?.length && !notAllowCustomCredential}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
title: customModel ? t('common.modelProvider.auth.modelCredentials') : t('common.modelProvider.auth.apiKeys'),
|
title: isCustomModel ? '' : t('common.modelProvider.auth.apiKeys'),
|
||||||
model: customModel ? model : undefined,
|
model: isCustomModel ? model : undefined,
|
||||||
credentials: available_credentials ?? [],
|
credentials: available_credentials ?? [],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
showModelTitle={!isCustomModel}
|
||||||
configurationMethod={configurationMethod}
|
configurationMethod={configurationMethod}
|
||||||
currentCustomConfigurationModelFixedFields={customModel ? {
|
currentCustomConfigurationModelFixedFields={isCustomModel ? {
|
||||||
__model_name: model.model,
|
__model_name: model.model,
|
||||||
__model_type: model.model_type,
|
__model_type: model.model_type,
|
||||||
} : undefined}
|
} : undefined}
|
||||||
onItemClick={onSelectCredential}
|
onItemClick={onSelectCredential}
|
||||||
placement='bottom-start'
|
placement='bottom-start'
|
||||||
onUpdate={onUpdate}
|
popupTitle={isCustomModel ? t('common.modelProvider.auth.modelCredentials') : ''}
|
||||||
isModelCredential={customModel}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,39 @@
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
RiAddCircleFill,
|
RiAddCircleFill,
|
||||||
|
RiAddLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
} from '@/app/components/base/button'
|
} from '@/app/components/base/button'
|
||||||
import type {
|
import type {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import Authorized from './authorized'
|
|
||||||
import {
|
|
||||||
useAuth,
|
|
||||||
useCustomModels,
|
|
||||||
} from './hooks'
|
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import ModelIcon from '../model-icon'
|
||||||
|
import { useCanAddedModels } from './hooks/use-custom-models'
|
||||||
|
import { useAuth } from './hooks/use-auth'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
type AddCustomModelProps = {
|
type AddCustomModelProps = {
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
open?: boolean
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
}
|
}
|
||||||
const AddCustomModel = ({
|
const AddCustomModel = ({
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -34,44 +41,32 @@ const AddCustomModel = ({
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
}: AddCustomModelProps) => {
|
}: AddCustomModelProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const customModels = useCustomModels(provider)
|
const [open, setOpen] = useState(false)
|
||||||
const noModels = !customModels.length
|
const canAddedModels = useCanAddedModels(provider)
|
||||||
|
const noModels = !canAddedModels.length
|
||||||
const {
|
const {
|
||||||
handleOpenModal,
|
handleOpenModal: handleOpenModalForAddNewCustomModel,
|
||||||
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields, true)
|
} = useAuth(
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
{
|
||||||
|
isModelCredential: true,
|
||||||
|
mode: ModelModalModeEnum.configCustomModel,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const {
|
||||||
|
handleOpenModal: handleOpenModalForAddCustomModelToModelList,
|
||||||
|
} = useAuth(
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
{
|
||||||
|
isModelCredential: true,
|
||||||
|
mode: ModelModalModeEnum.addCustomModelToModelList,
|
||||||
|
},
|
||||||
|
)
|
||||||
const notAllowCustomCredential = provider.allow_custom_token === false
|
const notAllowCustomCredential = provider.allow_custom_token === false
|
||||||
const handleClick = useCallback(() => {
|
|
||||||
if (notAllowCustomCredential)
|
|
||||||
return
|
|
||||||
|
|
||||||
handleOpenModal()
|
|
||||||
}, [handleOpenModal, notAllowCustomCredential])
|
|
||||||
const ButtonComponent = useMemo(() => {
|
|
||||||
const Item = (
|
|
||||||
<Button
|
|
||||||
variant='ghost-accent'
|
|
||||||
size='small'
|
|
||||||
onClick={handleClick}
|
|
||||||
className={cn(
|
|
||||||
notAllowCustomCredential && 'cursor-not-allowed opacity-50',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{t('common.modelProvider.addModel')}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
if (notAllowCustomCredential) {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
asChild
|
|
||||||
popupContent={t('plugin.auth.credentialUnavailable')}
|
|
||||||
>
|
|
||||||
{Item}
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return Item
|
|
||||||
}, [handleClick, notAllowCustomCredential, t])
|
|
||||||
|
|
||||||
const renderTrigger = useCallback((open?: boolean) => {
|
const renderTrigger = useCallback((open?: boolean) => {
|
||||||
const Item = (
|
const Item = (
|
||||||
|
|
@ -79,32 +74,93 @@ const AddCustomModel = ({
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
size='small'
|
size='small'
|
||||||
className={cn(
|
className={cn(
|
||||||
|
'text-text-tertiary',
|
||||||
open && 'bg-components-button-ghost-bg-hover',
|
open && 'bg-components-button-ghost-bg-hover',
|
||||||
|
notAllowCustomCredential && !!noModels && 'cursor-not-allowed opacity-50',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
|
<RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
|
||||||
{t('common.modelProvider.addModel')}
|
{t('common.modelProvider.addModel')}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
if (notAllowCustomCredential && !!noModels) {
|
||||||
|
return (
|
||||||
|
<Tooltip asChild popupContent={t('plugin.auth.credentialUnavailable')}>
|
||||||
|
{Item}
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
return Item
|
return Item
|
||||||
}, [t])
|
}, [t, notAllowCustomCredential, noModels])
|
||||||
|
|
||||||
if (noModels)
|
|
||||||
return ButtonComponent
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Authorized
|
<PortalToFollowElem
|
||||||
provider={provider}
|
open={open}
|
||||||
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
onOpenChange={setOpen}
|
||||||
items={customModels.map(model => ({
|
placement='bottom-end'
|
||||||
model,
|
offset={{
|
||||||
credentials: model.available_model_credentials ?? [],
|
mainAxis: 4,
|
||||||
}))}
|
crossAxis: 0,
|
||||||
renderTrigger={renderTrigger}
|
}}
|
||||||
isModelCredential
|
>
|
||||||
enableAddModelCredential
|
<PortalToFollowElemTrigger onClick={() => {
|
||||||
bottomAddModelCredentialText={t('common.modelProvider.auth.addNewModel')}
|
if (noModels) {
|
||||||
/>
|
if (notAllowCustomCredential)
|
||||||
|
return
|
||||||
|
handleOpenModalForAddNewCustomModel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(prev => !prev)
|
||||||
|
}}>
|
||||||
|
{renderTrigger(open)}
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
|
<div className='w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
|
||||||
|
<div className='max-h-[304px] overflow-y-auto p-1'>
|
||||||
|
{
|
||||||
|
canAddedModels.map(model => (
|
||||||
|
<div
|
||||||
|
key={model.model}
|
||||||
|
className='flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover'
|
||||||
|
onClick={() => {
|
||||||
|
handleOpenModalForAddCustomModelToModelList(undefined, model)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModelIcon
|
||||||
|
className='mr-1 h-5 w-5 shrink-0'
|
||||||
|
iconClassName='h-5 w-5'
|
||||||
|
provider={provider}
|
||||||
|
modelName={model.model}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className='system-md-regular grow truncate text-text-primary'
|
||||||
|
title={model.model}
|
||||||
|
>
|
||||||
|
{model.model}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
!notAllowCustomCredential && (
|
||||||
|
<div
|
||||||
|
className='system-xs-medium flex cursor-pointer items-center border-t border-t-divider-subtle p-3 text-text-accent-light-mode-only'
|
||||||
|
onClick={() => {
|
||||||
|
handleOpenModalForAddNewCustomModel()
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiAddLine className='mr-1 h-4 w-4' />
|
||||||
|
{t('common.modelProvider.auth.addNewModel')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,17 @@ import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { RiAddLine } from '@remixicon/react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import CredentialItem from './credential-item'
|
import CredentialItem from './credential-item'
|
||||||
import type {
|
import type {
|
||||||
Credential,
|
Credential,
|
||||||
CustomModel,
|
CustomModel,
|
||||||
CustomModelCredential,
|
CustomModelCredential,
|
||||||
|
ModelProvider,
|
||||||
} from '../../declarations'
|
} from '../../declarations'
|
||||||
import Button from '@/app/components/base/button'
|
import ModelIcon from '../../model-icon'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
|
||||||
|
|
||||||
type AuthorizedItemProps = {
|
type AuthorizedItemProps = {
|
||||||
|
provider: ModelProvider
|
||||||
model?: CustomModelCredential
|
model?: CustomModelCredential
|
||||||
title?: string
|
title?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|
@ -25,8 +24,12 @@ type AuthorizedItemProps = {
|
||||||
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
||||||
enableAddModelCredential?: boolean
|
enableAddModelCredential?: boolean
|
||||||
notAllowCustomCredential?: boolean
|
notAllowCustomCredential?: boolean
|
||||||
|
showModelTitle?: boolean
|
||||||
|
disableDeleteButShowAction?: boolean
|
||||||
|
disableDeleteTip?: string
|
||||||
}
|
}
|
||||||
export const AuthorizedItem = ({
|
export const AuthorizedItem = ({
|
||||||
|
provider,
|
||||||
model,
|
model,
|
||||||
title,
|
title,
|
||||||
credentials,
|
credentials,
|
||||||
|
|
@ -36,10 +39,10 @@ export const AuthorizedItem = ({
|
||||||
showItemSelectedIcon,
|
showItemSelectedIcon,
|
||||||
selectedCredentialId,
|
selectedCredentialId,
|
||||||
onItemClick,
|
onItemClick,
|
||||||
enableAddModelCredential,
|
showModelTitle,
|
||||||
notAllowCustomCredential,
|
disableDeleteButShowAction,
|
||||||
|
disableDeleteTip,
|
||||||
}: AuthorizedItemProps) => {
|
}: AuthorizedItemProps) => {
|
||||||
const { t } = useTranslation()
|
|
||||||
const handleEdit = useCallback((credential?: Credential) => {
|
const handleEdit = useCallback((credential?: Credential) => {
|
||||||
onEdit?.(credential, model)
|
onEdit?.(credential, model)
|
||||||
}, [onEdit, model])
|
}, [onEdit, model])
|
||||||
|
|
@ -52,34 +55,29 @@ export const AuthorizedItem = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<div
|
{
|
||||||
className='flex h-9 items-center'
|
showModelTitle && (
|
||||||
>
|
<div
|
||||||
<div className='h-5 w-5 shrink-0'></div>
|
className='flex h-9 items-center px-2'
|
||||||
<div
|
>
|
||||||
className='system-md-medium mx-1 grow truncate text-text-primary'
|
{
|
||||||
title={title ?? model?.model}
|
model?.model && (
|
||||||
>
|
<ModelIcon
|
||||||
{title ?? model?.model}
|
className='mr-1 h-5 w-5 shrink-0'
|
||||||
</div>
|
provider={provider}
|
||||||
{
|
modelName={model.model}
|
||||||
enableAddModelCredential && !notAllowCustomCredential && (
|
/>
|
||||||
<Tooltip
|
)
|
||||||
asChild
|
}
|
||||||
popupContent={t('common.modelProvider.auth.addModelCredential')}
|
<div
|
||||||
|
className='system-md-medium mx-1 grow truncate text-text-primary'
|
||||||
|
title={title ?? model?.model}
|
||||||
>
|
>
|
||||||
<Button
|
{title ?? model?.model}
|
||||||
className='h-6 w-6 shrink-0 rounded-full p-0'
|
</div>
|
||||||
size='small'
|
</div>
|
||||||
variant='secondary-accent'
|
)
|
||||||
onClick={() => handleEdit?.()}
|
}
|
||||||
>
|
|
||||||
<RiAddLine className='h-4 w-4' />
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{
|
{
|
||||||
credentials.map(credential => (
|
credentials.map(credential => (
|
||||||
<CredentialItem
|
<CredentialItem
|
||||||
|
|
@ -91,6 +89,8 @@ export const AuthorizedItem = ({
|
||||||
showSelectedIcon={showItemSelectedIcon}
|
showSelectedIcon={showItemSelectedIcon}
|
||||||
selectedCredentialId={selectedCredentialId}
|
selectedCredentialId={selectedCredentialId}
|
||||||
onItemClick={handleItemClick}
|
onItemClick={handleItemClick}
|
||||||
|
disableDeleteButShowAction={disableDeleteButShowAction}
|
||||||
|
disableDeleteTip={disableDeleteTip}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ type CredentialItemProps = {
|
||||||
disableRename?: boolean
|
disableRename?: boolean
|
||||||
disableEdit?: boolean
|
disableEdit?: boolean
|
||||||
disableDelete?: boolean
|
disableDelete?: boolean
|
||||||
|
disableDeleteButShowAction?: boolean
|
||||||
|
disableDeleteTip?: string
|
||||||
showSelectedIcon?: boolean
|
showSelectedIcon?: boolean
|
||||||
selectedCredentialId?: string
|
selectedCredentialId?: string
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +38,8 @@ const CredentialItem = ({
|
||||||
disableRename,
|
disableRename,
|
||||||
disableEdit,
|
disableEdit,
|
||||||
disableDelete,
|
disableDelete,
|
||||||
|
disableDeleteButShowAction,
|
||||||
|
disableDeleteTip,
|
||||||
showSelectedIcon,
|
showSelectedIcon,
|
||||||
selectedCredentialId,
|
selectedCredentialId,
|
||||||
}: CredentialItemProps) => {
|
}: CredentialItemProps) => {
|
||||||
|
|
@ -43,6 +47,9 @@ const CredentialItem = ({
|
||||||
const showAction = useMemo(() => {
|
const showAction = useMemo(() => {
|
||||||
return !(disableRename && disableEdit && disableDelete)
|
return !(disableRename && disableEdit && disableDelete)
|
||||||
}, [disableRename, disableEdit, disableDelete])
|
}, [disableRename, disableEdit, disableDelete])
|
||||||
|
const disableDeleteWhenSelected = useMemo(() => {
|
||||||
|
return disableDeleteButShowAction && selectedCredentialId === credential.credential_id
|
||||||
|
}, [disableDeleteButShowAction, selectedCredentialId, credential.credential_id])
|
||||||
|
|
||||||
const Item = (
|
const Item = (
|
||||||
<div
|
<div
|
||||||
|
|
@ -104,16 +111,21 @@ const CredentialItem = ({
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!disableDelete && !credential.from_enterprise && (
|
!disableDelete && !credential.from_enterprise && (
|
||||||
<Tooltip popupContent={t('common.operation.delete')}>
|
<Tooltip popupContent={disableDeleteWhenSelected ? disableDeleteTip : t('common.operation.delete')}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className='hover:bg-transparent'
|
className='hover:bg-transparent'
|
||||||
disabled={disabled}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
if (disabled || disableDeleteWhenSelected)
|
||||||
|
return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onDelete?.(credential)
|
onDelete?.(credential)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary hover:text-text-destructive' />
|
<RiDeleteBinLine className={cn(
|
||||||
|
'h-4 w-4 text-text-tertiary',
|
||||||
|
!disableDeleteWhenSelected && 'hover:text-text-destructive',
|
||||||
|
disableDeleteWhenSelected && 'opacity-50',
|
||||||
|
)} />
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import {
|
import {
|
||||||
|
Fragment,
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
RiAddLine,
|
RiAddLine,
|
||||||
RiEqualizer2Line,
|
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
|
|
@ -25,6 +24,7 @@ import type {
|
||||||
Credential,
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
CustomModel,
|
CustomModel,
|
||||||
|
ModelModalModeEnum,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../../declarations'
|
} from '../../declarations'
|
||||||
import { useAuth } from '../hooks'
|
import { useAuth } from '../hooks'
|
||||||
|
|
@ -34,15 +34,20 @@ type AuthorizedProps = {
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
isModelCredential?: boolean
|
authParams?: {
|
||||||
|
isModelCredential?: boolean
|
||||||
|
onUpdate?: (newPayload?: any, formValues?: Record<string, any>) => void
|
||||||
|
onRemove?: (credentialId: string) => void
|
||||||
|
mode?: ModelModalModeEnum
|
||||||
|
}
|
||||||
items: {
|
items: {
|
||||||
title?: string
|
title?: string
|
||||||
model?: CustomModel
|
model?: CustomModel
|
||||||
|
selectedCredential?: Credential
|
||||||
credentials: Credential[]
|
credentials: Credential[]
|
||||||
}[]
|
}[]
|
||||||
selectedCredential?: Credential
|
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
renderTrigger?: (open?: boolean) => React.ReactNode
|
renderTrigger: (open?: boolean) => React.ReactNode
|
||||||
isOpen?: boolean
|
isOpen?: boolean
|
||||||
onOpenChange?: (open: boolean) => void
|
onOpenChange?: (open: boolean) => void
|
||||||
offset?: PortalToFollowElemOptions['offset']
|
offset?: PortalToFollowElemOptions['offset']
|
||||||
|
|
@ -50,18 +55,22 @@ type AuthorizedProps = {
|
||||||
triggerPopupSameWidth?: boolean
|
triggerPopupSameWidth?: boolean
|
||||||
popupClassName?: string
|
popupClassName?: string
|
||||||
showItemSelectedIcon?: boolean
|
showItemSelectedIcon?: boolean
|
||||||
onUpdate?: () => void
|
|
||||||
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
||||||
enableAddModelCredential?: boolean
|
enableAddModelCredential?: boolean
|
||||||
bottomAddModelCredentialText?: string
|
triggerOnlyOpenModal?: boolean
|
||||||
|
hideAddAction?: boolean
|
||||||
|
disableItemClick?: boolean
|
||||||
|
popupTitle?: string
|
||||||
|
showModelTitle?: boolean
|
||||||
|
disableDeleteButShowAction?: boolean
|
||||||
|
disableDeleteTip?: string
|
||||||
}
|
}
|
||||||
const Authorized = ({
|
const Authorized = ({
|
||||||
provider,
|
provider,
|
||||||
configurationMethod,
|
configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
items,
|
items,
|
||||||
isModelCredential,
|
authParams,
|
||||||
selectedCredential,
|
|
||||||
disabled,
|
disabled,
|
||||||
renderTrigger,
|
renderTrigger,
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
@ -71,10 +80,14 @@ const Authorized = ({
|
||||||
triggerPopupSameWidth = false,
|
triggerPopupSameWidth = false,
|
||||||
popupClassName,
|
popupClassName,
|
||||||
showItemSelectedIcon,
|
showItemSelectedIcon,
|
||||||
onUpdate,
|
|
||||||
onItemClick,
|
onItemClick,
|
||||||
enableAddModelCredential,
|
triggerOnlyOpenModal,
|
||||||
bottomAddModelCredentialText,
|
hideAddAction,
|
||||||
|
disableItemClick,
|
||||||
|
popupTitle,
|
||||||
|
showModelTitle,
|
||||||
|
disableDeleteButShowAction,
|
||||||
|
disableDeleteTip,
|
||||||
}: AuthorizedProps) => {
|
}: AuthorizedProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [isLocalOpen, setIsLocalOpen] = useState(false)
|
const [isLocalOpen, setIsLocalOpen] = useState(false)
|
||||||
|
|
@ -85,6 +98,12 @@ const Authorized = ({
|
||||||
|
|
||||||
setIsLocalOpen(open)
|
setIsLocalOpen(open)
|
||||||
}, [onOpenChange])
|
}, [onOpenChange])
|
||||||
|
const {
|
||||||
|
isModelCredential,
|
||||||
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
|
mode,
|
||||||
|
} = authParams || {}
|
||||||
const {
|
const {
|
||||||
openConfirmDelete,
|
openConfirmDelete,
|
||||||
closeConfirmDelete,
|
closeConfirmDelete,
|
||||||
|
|
@ -93,7 +112,17 @@ const Authorized = ({
|
||||||
handleConfirmDelete,
|
handleConfirmDelete,
|
||||||
deleteCredentialId,
|
deleteCredentialId,
|
||||||
handleOpenModal,
|
handleOpenModal,
|
||||||
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onUpdate)
|
} = useAuth(
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
{
|
||||||
|
isModelCredential,
|
||||||
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
|
mode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
const handleEdit = useCallback((credential?: Credential, model?: CustomModel) => {
|
const handleEdit = useCallback((credential?: Credential, model?: CustomModel) => {
|
||||||
handleOpenModal(credential, model)
|
handleOpenModal(credential, model)
|
||||||
|
|
@ -101,28 +130,18 @@ const Authorized = ({
|
||||||
}, [handleOpenModal, setMergedIsOpen])
|
}, [handleOpenModal, setMergedIsOpen])
|
||||||
|
|
||||||
const handleItemClick = useCallback((credential: Credential, model?: CustomModel) => {
|
const handleItemClick = useCallback((credential: Credential, model?: CustomModel) => {
|
||||||
|
if (disableItemClick)
|
||||||
|
return
|
||||||
|
|
||||||
if (onItemClick)
|
if (onItemClick)
|
||||||
onItemClick(credential, model)
|
onItemClick(credential, model)
|
||||||
else
|
else
|
||||||
handleActiveCredential(credential, model)
|
handleActiveCredential(credential, model)
|
||||||
|
|
||||||
setMergedIsOpen(false)
|
setMergedIsOpen(false)
|
||||||
}, [handleActiveCredential, onItemClick, setMergedIsOpen])
|
}, [handleActiveCredential, onItemClick, setMergedIsOpen, disableItemClick])
|
||||||
const notAllowCustomCredential = provider.allow_custom_token === false
|
const notAllowCustomCredential = provider.allow_custom_token === false
|
||||||
|
|
||||||
const Trigger = useMemo(() => {
|
|
||||||
const Item = (
|
|
||||||
<Button
|
|
||||||
className='grow'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{t('common.operation.config')}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
return Item
|
|
||||||
}, [t])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PortalToFollowElem
|
<PortalToFollowElem
|
||||||
|
|
@ -134,44 +153,60 @@ const Authorized = ({
|
||||||
>
|
>
|
||||||
<PortalToFollowElemTrigger
|
<PortalToFollowElemTrigger
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (triggerOnlyOpenModal) {
|
||||||
|
handleOpenModal()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setMergedIsOpen(!mergedIsOpen)
|
setMergedIsOpen(!mergedIsOpen)
|
||||||
}}
|
}}
|
||||||
asChild
|
asChild
|
||||||
>
|
>
|
||||||
{
|
{renderTrigger(mergedIsOpen)}
|
||||||
renderTrigger
|
|
||||||
? renderTrigger(mergedIsOpen)
|
|
||||||
: Trigger
|
|
||||||
}
|
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='z-[100]'>
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
|
'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]',
|
||||||
popupClassName,
|
popupClassName,
|
||||||
)}>
|
)}>
|
||||||
|
{
|
||||||
|
popupTitle && (
|
||||||
|
<div className='system-xs-medium px-3 pb-0.5 pt-[10px] text-text-tertiary'>
|
||||||
|
{popupTitle}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div className='max-h-[304px] overflow-y-auto'>
|
<div className='max-h-[304px] overflow-y-auto'>
|
||||||
{
|
{
|
||||||
items.map((item, index) => (
|
items.map((item, index) => (
|
||||||
<AuthorizedItem
|
<Fragment key={index}>
|
||||||
key={index}
|
<AuthorizedItem
|
||||||
title={item.title}
|
provider={provider}
|
||||||
model={item.model}
|
title={item.title}
|
||||||
credentials={item.credentials}
|
model={item.model}
|
||||||
disabled={disabled}
|
credentials={item.credentials}
|
||||||
onDelete={openConfirmDelete}
|
disabled={disabled}
|
||||||
onEdit={handleEdit}
|
onDelete={openConfirmDelete}
|
||||||
showItemSelectedIcon={showItemSelectedIcon}
|
disableDeleteButShowAction={disableDeleteButShowAction}
|
||||||
selectedCredentialId={selectedCredential?.credential_id}
|
disableDeleteTip={disableDeleteTip}
|
||||||
onItemClick={handleItemClick}
|
onEdit={handleEdit}
|
||||||
enableAddModelCredential={enableAddModelCredential}
|
showItemSelectedIcon={showItemSelectedIcon}
|
||||||
notAllowCustomCredential={notAllowCustomCredential}
|
selectedCredentialId={item.selectedCredential?.credential_id}
|
||||||
/>
|
onItemClick={handleItemClick}
|
||||||
|
showModelTitle={showModelTitle}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
index !== items.length - 1 && (
|
||||||
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className='h-[1px] bg-divider-subtle'></div>
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
{
|
{
|
||||||
isModelCredential && !notAllowCustomCredential && (
|
isModelCredential && !notAllowCustomCredential && !hideAddAction && (
|
||||||
<div
|
<div
|
||||||
onClick={() => handleEdit(
|
onClick={() => handleEdit(
|
||||||
undefined,
|
undefined,
|
||||||
|
|
@ -182,15 +217,15 @@ const Authorized = ({
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
)}
|
)}
|
||||||
className='system-xs-medium flex h-[30px] cursor-pointer items-center px-3 text-text-accent-light-mode-only'
|
className='system-xs-medium flex h-[40px] cursor-pointer items-center px-3 text-text-accent-light-mode-only'
|
||||||
>
|
>
|
||||||
<RiAddLine className='mr-1 h-4 w-4' />
|
<RiAddLine className='mr-1 h-4 w-4' />
|
||||||
{bottomAddModelCredentialText ?? t('common.modelProvider.auth.addModelCredential')}
|
{t('common.modelProvider.auth.addModelCredential')}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!isModelCredential && !notAllowCustomCredential && (
|
!isModelCredential && !notAllowCustomCredential && !hideAddAction && (
|
||||||
<div className='p-2'>
|
<div className='p-2'>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleEdit()}
|
onClick={() => handleEdit()}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const ConfigModel = ({
|
||||||
if (loadBalancingInvalid) {
|
if (loadBalancingInvalid) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='system-2xs-medium-uppercase relative flex h-[18px] items-center rounded-[5px] border border-text-warning bg-components-badge-bg-dimm px-1.5 text-text-warning'
|
className='system-2xs-medium-uppercase relative flex h-[18px] cursor-pointer items-center rounded-[5px] border border-text-warning bg-components-badge-bg-dimm px-1.5 text-text-warning'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<RiScales3Line className='mr-0.5 h-3 w-3' />
|
<RiScales3Line className='mr-0.5 h-3 w-3' />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo,
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
|
|
@ -16,24 +15,18 @@ import type {
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import Authorized from './authorized'
|
import Authorized from './authorized'
|
||||||
import { useAuth, useCredentialStatus } from './hooks'
|
import { useCredentialStatus } from './hooks'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
|
|
||||||
type ConfigProviderProps = {
|
type ConfigProviderProps = {
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
}
|
}
|
||||||
const ConfigProvider = ({
|
const ConfigProvider = ({
|
||||||
provider,
|
provider,
|
||||||
configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
}: ConfigProviderProps) => {
|
}: ConfigProviderProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
|
||||||
handleOpenModal,
|
|
||||||
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields)
|
|
||||||
const {
|
const {
|
||||||
hasCredential,
|
hasCredential,
|
||||||
authorized,
|
authorized,
|
||||||
|
|
@ -42,23 +35,20 @@ const ConfigProvider = ({
|
||||||
available_credentials,
|
available_credentials,
|
||||||
} = useCredentialStatus(provider)
|
} = useCredentialStatus(provider)
|
||||||
const notAllowCustomCredential = provider.allow_custom_token === false
|
const notAllowCustomCredential = provider.allow_custom_token === false
|
||||||
const handleClick = useCallback(() => {
|
|
||||||
if (!hasCredential && !notAllowCustomCredential)
|
const renderTrigger = useCallback(() => {
|
||||||
handleOpenModal()
|
|
||||||
}, [handleOpenModal, hasCredential, notAllowCustomCredential])
|
|
||||||
const ButtonComponent = useMemo(() => {
|
|
||||||
const Item = (
|
const Item = (
|
||||||
<Button
|
<Button
|
||||||
className={cn('grow', notAllowCustomCredential && 'cursor-not-allowed opacity-50')}
|
className='grow'
|
||||||
size='small'
|
size='small'
|
||||||
onClick={handleClick}
|
|
||||||
variant={!authorized ? 'secondary-accent' : 'secondary'}
|
variant={!authorized ? 'secondary-accent' : 'secondary'}
|
||||||
>
|
>
|
||||||
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
||||||
{t('common.operation.setup')}
|
{hasCredential && t('common.operation.config')}
|
||||||
|
{!hasCredential && t('common.operation.setup')}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
if (notAllowCustomCredential) {
|
if (notAllowCustomCredential && !hasCredential) {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
asChild
|
asChild
|
||||||
|
|
@ -69,26 +59,27 @@ const ConfigProvider = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return Item
|
return Item
|
||||||
}, [handleClick, authorized, notAllowCustomCredential, t])
|
}, [authorized, hasCredential, notAllowCustomCredential, t])
|
||||||
|
|
||||||
if (!hasCredential)
|
|
||||||
return ButtonComponent
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Authorized
|
<Authorized
|
||||||
provider={provider}
|
provider={provider}
|
||||||
configurationMethod={ConfigurationMethodEnum.predefinedModel}
|
configurationMethod={ConfigurationMethodEnum.predefinedModel}
|
||||||
|
currentCustomConfigurationModelFixedFields={currentCustomConfigurationModelFixedFields}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
title: t('common.modelProvider.auth.apiKeys'),
|
title: t('common.modelProvider.auth.apiKeys'),
|
||||||
credentials: available_credentials ?? [],
|
credentials: available_credentials ?? [],
|
||||||
|
selectedCredential: {
|
||||||
|
credential_id: current_credential_id ?? '',
|
||||||
|
credential_name: current_credential_name ?? '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
selectedCredential={{
|
|
||||||
credential_id: current_credential_id ?? '',
|
|
||||||
credential_name: current_credential_name ?? '',
|
|
||||||
}}
|
|
||||||
showItemSelectedIcon
|
showItemSelectedIcon
|
||||||
|
showModelTitle
|
||||||
|
renderTrigger={renderTrigger}
|
||||||
|
triggerOnlyOpenModal={!hasCredential && !notAllowCustomCredential}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiAddLine,
|
||||||
|
RiArrowDownSLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import type { Credential } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import CredentialItem from './authorized/credential-item'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
|
||||||
|
type CredentialSelectorProps = {
|
||||||
|
selectedCredential?: Credential & { addNewCredential?: boolean }
|
||||||
|
credentials: Credential[]
|
||||||
|
onSelect: (credential: Credential & { addNewCredential?: boolean }) => void
|
||||||
|
disabled?: boolean
|
||||||
|
notAllowAddNewCredential?: boolean
|
||||||
|
}
|
||||||
|
const CredentialSelector = ({
|
||||||
|
selectedCredential,
|
||||||
|
credentials,
|
||||||
|
onSelect,
|
||||||
|
disabled,
|
||||||
|
notAllowAddNewCredential,
|
||||||
|
}: CredentialSelectorProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const handleSelect = useCallback((credential: Credential & { addNewCredential?: boolean }) => {
|
||||||
|
setOpen(false)
|
||||||
|
onSelect(credential)
|
||||||
|
}, [onSelect])
|
||||||
|
const handleAddNewCredential = useCallback(() => {
|
||||||
|
handleSelect({
|
||||||
|
credential_id: '__add_new_credential',
|
||||||
|
addNewCredential: true,
|
||||||
|
credential_name: t('common.modelProvider.auth.addNewModelCredential'),
|
||||||
|
})
|
||||||
|
}, [handleSelect, t])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
triggerPopupSameWidth
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger asChild onClick={() => !disabled && setOpen(v => !v)}>
|
||||||
|
<div className='system-sm-regular flex h-8 w-full items-center justify-between rounded-lg bg-components-input-bg-normal px-2'>
|
||||||
|
{
|
||||||
|
selectedCredential && (
|
||||||
|
<div className='flex items-center'>
|
||||||
|
{
|
||||||
|
!selectedCredential.addNewCredential && <Indicator className='ml-1 mr-2 shrink-0' />
|
||||||
|
}
|
||||||
|
<div className='system-sm-regular truncate text-components-input-text-filled' title={selectedCredential.credential_name}>{selectedCredential.credential_name}</div>
|
||||||
|
{
|
||||||
|
selectedCredential.from_enterprise && (
|
||||||
|
<Badge className='shrink-0'>Enterprise</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!selectedCredential && (
|
||||||
|
<div className='system-sm-regular grow truncate text-components-input-text-placeholder'>{t('common.modelProvider.auth.selectModelCredential')}</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<RiArrowDownSLine className='h-4 w-4 text-text-quaternary' />
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
|
<div className='border-ccomponents-panel-border rounded-xl border-[0.5px] bg-components-panel-bg-blur shadow-lg'>
|
||||||
|
<div className='max-h-[320px] overflow-y-auto p-1'>
|
||||||
|
{
|
||||||
|
credentials.map(credential => (
|
||||||
|
<CredentialItem
|
||||||
|
key={credential.credential_id}
|
||||||
|
credential={credential}
|
||||||
|
disableDelete
|
||||||
|
disableEdit
|
||||||
|
disableRename
|
||||||
|
onItemClick={handleSelect}
|
||||||
|
showSelectedIcon
|
||||||
|
selectedCredentialId={selectedCredential?.credential_id}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
!notAllowAddNewCredential && (
|
||||||
|
<div
|
||||||
|
className='system-xs-medium flex h-10 cursor-pointer items-center border-t border-t-divider-subtle px-7 text-text-accent-light-mode-only'
|
||||||
|
onClick={handleAddNewCredential}
|
||||||
|
>
|
||||||
|
<RiAddLine className='mr-1 h-4 w-4' />
|
||||||
|
{t('common.modelProvider.auth.addNewModelCredential')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(CredentialSelector)
|
||||||
|
|
@ -17,7 +17,7 @@ import type {
|
||||||
|
|
||||||
export const useGetCredential = (provider: string, isModelCredential?: boolean, credentialId?: string, model?: CustomModel, configFrom?: string) => {
|
export const useGetCredential = (provider: string, isModelCredential?: boolean, credentialId?: string, model?: CustomModel, configFrom?: string) => {
|
||||||
const providerData = useGetProviderCredential(!isModelCredential && !!credentialId, provider, credentialId)
|
const providerData = useGetProviderCredential(!isModelCredential && !!credentialId, provider, credentialId)
|
||||||
const modelData = useGetModelCredential(!!isModelCredential && !!credentialId, provider, credentialId, model?.model, model?.model_type, configFrom)
|
const modelData = useGetModelCredential(!!isModelCredential && (!!credentialId || !!model), provider, credentialId, model?.model, model?.model_type, configFrom)
|
||||||
return isModelCredential ? modelData : providerData
|
return isModelCredential ? modelData : providerData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,32 @@ import type {
|
||||||
Credential,
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
CustomModel,
|
CustomModel,
|
||||||
|
ModelModalModeEnum,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../../declarations'
|
} from '../../declarations'
|
||||||
import {
|
import {
|
||||||
useModelModalHandler,
|
useModelModalHandler,
|
||||||
useRefreshModel,
|
useRefreshModel,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
|
import { useDeleteModel } from '@/service/use-models'
|
||||||
|
|
||||||
export const useAuth = (
|
export const useAuth = (
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
isModelCredential?: boolean,
|
extra: {
|
||||||
onUpdate?: () => void,
|
isModelCredential?: boolean,
|
||||||
|
onUpdate?: (newPayload?: any, formValues?: Record<string, any>) => void,
|
||||||
|
onRemove?: (credentialId: string) => void,
|
||||||
|
mode?: ModelModalModeEnum,
|
||||||
|
} = {},
|
||||||
) => {
|
) => {
|
||||||
|
const {
|
||||||
|
isModelCredential,
|
||||||
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
|
mode,
|
||||||
|
} = extra
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const {
|
const {
|
||||||
|
|
@ -33,22 +45,30 @@ export const useAuth = (
|
||||||
getEditCredentialService,
|
getEditCredentialService,
|
||||||
getAddCredentialService,
|
getAddCredentialService,
|
||||||
} = useAuthService(provider.provider)
|
} = useAuthService(provider.provider)
|
||||||
|
const { mutateAsync: deleteModelService } = useDeleteModel(provider.provider)
|
||||||
const handleOpenModelModal = useModelModalHandler()
|
const handleOpenModelModal = useModelModalHandler()
|
||||||
const { handleRefreshModel } = useRefreshModel()
|
const { handleRefreshModel } = useRefreshModel()
|
||||||
const pendingOperationCredentialId = useRef<string | null>(null)
|
const pendingOperationCredentialId = useRef<string | null>(null)
|
||||||
const pendingOperationModel = useRef<CustomModel | null>(null)
|
|
||||||
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||||
|
const handleSetDeleteCredentialId = useCallback((credentialId: string | null) => {
|
||||||
|
setDeleteCredentialId(credentialId)
|
||||||
|
pendingOperationCredentialId.current = credentialId
|
||||||
|
}, [])
|
||||||
|
const pendingOperationModel = useRef<CustomModel | null>(null)
|
||||||
|
const [deleteModel, setDeleteModel] = useState<CustomModel | null>(null)
|
||||||
|
const handleSetDeleteModel = useCallback((model: CustomModel | null) => {
|
||||||
|
setDeleteModel(model)
|
||||||
|
pendingOperationModel.current = model
|
||||||
|
}, [])
|
||||||
const openConfirmDelete = useCallback((credential?: Credential, model?: CustomModel) => {
|
const openConfirmDelete = useCallback((credential?: Credential, model?: CustomModel) => {
|
||||||
if (credential)
|
if (credential)
|
||||||
pendingOperationCredentialId.current = credential.credential_id
|
handleSetDeleteCredentialId(credential.credential_id)
|
||||||
if (model)
|
if (model)
|
||||||
pendingOperationModel.current = model
|
handleSetDeleteModel(model)
|
||||||
|
|
||||||
setDeleteCredentialId(pendingOperationCredentialId.current)
|
|
||||||
}, [])
|
}, [])
|
||||||
const closeConfirmDelete = useCallback(() => {
|
const closeConfirmDelete = useCallback(() => {
|
||||||
setDeleteCredentialId(null)
|
handleSetDeleteCredentialId(null)
|
||||||
pendingOperationCredentialId.current = null
|
handleSetDeleteModel(null)
|
||||||
}, [])
|
}, [])
|
||||||
const [doingAction, setDoingAction] = useState(false)
|
const [doingAction, setDoingAction] = useState(false)
|
||||||
const doingActionRef = useRef(doingAction)
|
const doingActionRef = useRef(doingAction)
|
||||||
|
|
@ -70,45 +90,49 @@ export const useAuth = (
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: t('common.api.actionSuccess'),
|
message: t('common.api.actionSuccess'),
|
||||||
})
|
})
|
||||||
onUpdate?.()
|
|
||||||
handleRefreshModel(provider, configurationMethod, undefined)
|
handleRefreshModel(provider, configurationMethod, undefined)
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
handleSetDoingAction(false)
|
handleSetDoingAction(false)
|
||||||
}
|
}
|
||||||
}, [getActiveCredentialService, onUpdate, notify, t, handleSetDoingAction])
|
}, [getActiveCredentialService, notify, t, handleSetDoingAction])
|
||||||
const handleConfirmDelete = useCallback(async () => {
|
const handleConfirmDelete = useCallback(async () => {
|
||||||
if (doingActionRef.current)
|
if (doingActionRef.current)
|
||||||
return
|
return
|
||||||
if (!pendingOperationCredentialId.current) {
|
if (!pendingOperationCredentialId.current && !pendingOperationModel.current) {
|
||||||
setDeleteCredentialId(null)
|
closeConfirmDelete()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
handleSetDoingAction(true)
|
handleSetDoingAction(true)
|
||||||
await getDeleteCredentialService(!!isModelCredential)({
|
let payload: any = {}
|
||||||
credential_id: pendingOperationCredentialId.current,
|
if (pendingOperationCredentialId.current) {
|
||||||
model: pendingOperationModel.current?.model,
|
payload = {
|
||||||
model_type: pendingOperationModel.current?.model_type,
|
credential_id: pendingOperationCredentialId.current,
|
||||||
})
|
model: pendingOperationModel.current?.model,
|
||||||
|
model_type: pendingOperationModel.current?.model_type,
|
||||||
|
}
|
||||||
|
await getDeleteCredentialService(!!isModelCredential)(payload)
|
||||||
|
}
|
||||||
|
if (!pendingOperationCredentialId.current && pendingOperationModel.current) {
|
||||||
|
payload = {
|
||||||
|
model: pendingOperationModel.current.model,
|
||||||
|
model_type: pendingOperationModel.current.model_type,
|
||||||
|
}
|
||||||
|
await deleteModelService(payload)
|
||||||
|
}
|
||||||
notify({
|
notify({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: t('common.api.actionSuccess'),
|
message: t('common.api.actionSuccess'),
|
||||||
})
|
})
|
||||||
onUpdate?.()
|
|
||||||
handleRefreshModel(provider, configurationMethod, undefined)
|
handleRefreshModel(provider, configurationMethod, undefined)
|
||||||
setDeleteCredentialId(null)
|
onRemove?.(pendingOperationCredentialId.current ?? '')
|
||||||
pendingOperationCredentialId.current = null
|
closeConfirmDelete()
|
||||||
pendingOperationModel.current = null
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
handleSetDoingAction(false)
|
handleSetDoingAction(false)
|
||||||
}
|
}
|
||||||
}, [onUpdate, notify, t, handleSetDoingAction, getDeleteCredentialService, isModelCredential])
|
}, [notify, t, handleSetDoingAction, getDeleteCredentialService, isModelCredential, closeConfirmDelete, handleRefreshModel, provider, configurationMethod, deleteModelService])
|
||||||
const handleAddCredential = useCallback((model?: CustomModel) => {
|
|
||||||
if (model)
|
|
||||||
pendingOperationModel.current = model
|
|
||||||
}, [])
|
|
||||||
const handleSaveCredential = useCallback(async (payload: Record<string, any>) => {
|
const handleSaveCredential = useCallback(async (payload: Record<string, any>) => {
|
||||||
if (doingActionRef.current)
|
if (doingActionRef.current)
|
||||||
return
|
return
|
||||||
|
|
@ -123,24 +147,35 @@ export const useAuth = (
|
||||||
|
|
||||||
if (res.result === 'success') {
|
if (res.result === 'success') {
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
onUpdate?.()
|
handleRefreshModel(provider, configurationMethod, undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
handleSetDoingAction(false)
|
handleSetDoingAction(false)
|
||||||
}
|
}
|
||||||
}, [onUpdate, notify, t, handleSetDoingAction, getEditCredentialService, getAddCredentialService])
|
}, [notify, t, handleSetDoingAction, getEditCredentialService, getAddCredentialService])
|
||||||
const handleOpenModal = useCallback((credential?: Credential, model?: CustomModel) => {
|
const handleOpenModal = useCallback((credential?: Credential, model?: CustomModel) => {
|
||||||
handleOpenModelModal(
|
handleOpenModelModal(
|
||||||
provider,
|
provider,
|
||||||
configurationMethod,
|
configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
isModelCredential,
|
{
|
||||||
credential,
|
isModelCredential,
|
||||||
model,
|
credential,
|
||||||
onUpdate,
|
model,
|
||||||
|
onUpdate,
|
||||||
|
mode,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}, [handleOpenModelModal, provider, configurationMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onUpdate])
|
}, [
|
||||||
|
handleOpenModelModal,
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
isModelCredential,
|
||||||
|
onUpdate,
|
||||||
|
mode,
|
||||||
|
])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pendingOperationCredentialId,
|
pendingOperationCredentialId,
|
||||||
|
|
@ -150,8 +185,8 @@ export const useAuth = (
|
||||||
doingAction,
|
doingAction,
|
||||||
handleActiveCredential,
|
handleActiveCredential,
|
||||||
handleConfirmDelete,
|
handleConfirmDelete,
|
||||||
handleAddCredential,
|
|
||||||
deleteCredentialId,
|
deleteCredentialId,
|
||||||
|
deleteModel,
|
||||||
handleSaveCredential,
|
handleSaveCredential,
|
||||||
handleOpenModal,
|
handleOpenModal,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,9 @@ export const useCustomModels = (provider: ModelProvider) => {
|
||||||
|
|
||||||
return custom_models || []
|
return custom_models || []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useCanAddedModels = (provider: ModelProvider) => {
|
||||||
|
const { can_added_models } = provider.custom_configuration
|
||||||
|
|
||||||
|
return can_added_models || []
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next'
|
||||||
import type {
|
import type {
|
||||||
Credential,
|
Credential,
|
||||||
CustomModelCredential,
|
CustomModelCredential,
|
||||||
ModelLoadBalancingConfig,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../../declarations'
|
} from '../../declarations'
|
||||||
import {
|
import {
|
||||||
|
|
@ -18,7 +17,6 @@ export const useModelFormSchemas = (
|
||||||
credentials?: Record<string, any>,
|
credentials?: Record<string, any>,
|
||||||
credential?: Credential,
|
credential?: Credential,
|
||||||
model?: CustomModelCredential,
|
model?: CustomModelCredential,
|
||||||
draftConfig?: ModelLoadBalancingConfig,
|
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
|
@ -27,26 +25,15 @@ export const useModelFormSchemas = (
|
||||||
model_credential_schema,
|
model_credential_schema,
|
||||||
} = provider
|
} = provider
|
||||||
const formSchemas = useMemo(() => {
|
const formSchemas = useMemo(() => {
|
||||||
const modelTypeSchema = genModelTypeFormSchema(supported_model_types)
|
|
||||||
const modelNameSchema = genModelNameFormSchema(model_credential_schema?.model)
|
|
||||||
if (!!model) {
|
|
||||||
modelTypeSchema.disabled = true
|
|
||||||
modelNameSchema.disabled = true
|
|
||||||
}
|
|
||||||
return providerFormSchemaPredefined
|
return providerFormSchemaPredefined
|
||||||
? provider_credential_schema.credential_form_schemas
|
? provider_credential_schema.credential_form_schemas
|
||||||
: [
|
: model_credential_schema.credential_form_schemas
|
||||||
modelTypeSchema,
|
|
||||||
modelNameSchema,
|
|
||||||
...(draftConfig?.enabled ? [] : model_credential_schema.credential_form_schemas),
|
|
||||||
]
|
|
||||||
}, [
|
}, [
|
||||||
providerFormSchemaPredefined,
|
providerFormSchemaPredefined,
|
||||||
provider_credential_schema?.credential_form_schemas,
|
provider_credential_schema?.credential_form_schemas,
|
||||||
supported_model_types,
|
supported_model_types,
|
||||||
model_credential_schema?.credential_form_schemas,
|
model_credential_schema?.credential_form_schemas,
|
||||||
model_credential_schema?.model,
|
model_credential_schema?.model,
|
||||||
draftConfig?.enabled,
|
|
||||||
model,
|
model,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
@ -55,7 +42,7 @@ export const useModelFormSchemas = (
|
||||||
type: FormTypeEnum.textInput,
|
type: FormTypeEnum.textInput,
|
||||||
variable: '__authorization_name__',
|
variable: '__authorization_name__',
|
||||||
label: t('plugin.auth.authorizationName'),
|
label: t('plugin.auth.authorizationName'),
|
||||||
required: true,
|
required: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -79,8 +66,33 @@ export const useModelFormSchemas = (
|
||||||
return result
|
return result
|
||||||
}, [credentials, credential, model, formSchemas])
|
}, [credentials, credential, model, formSchemas])
|
||||||
|
|
||||||
|
const modelNameAndTypeFormSchemas = useMemo(() => {
|
||||||
|
if (providerFormSchemaPredefined)
|
||||||
|
return []
|
||||||
|
|
||||||
|
const modelNameSchema = genModelNameFormSchema(model_credential_schema?.model)
|
||||||
|
const modelTypeSchema = genModelTypeFormSchema(supported_model_types)
|
||||||
|
return [
|
||||||
|
modelNameSchema,
|
||||||
|
modelTypeSchema,
|
||||||
|
]
|
||||||
|
}, [supported_model_types, model_credential_schema?.model, providerFormSchemaPredefined])
|
||||||
|
|
||||||
|
const modelNameAndTypeFormValues = useMemo(() => {
|
||||||
|
let result = {}
|
||||||
|
if (providerFormSchemaPredefined)
|
||||||
|
return result
|
||||||
|
|
||||||
|
if (model)
|
||||||
|
result = { ...result, __model_name: model?.model, __model_type: model?.model_type }
|
||||||
|
|
||||||
|
return result
|
||||||
|
}, [model, providerFormSchemaPredefined])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
formSchemas: formSchemasWithAuthorizationName,
|
formSchemas: formSchemasWithAuthorizationName,
|
||||||
formValues,
|
formValues,
|
||||||
|
modelNameAndTypeFormSchemas,
|
||||||
|
modelNameAndTypeFormValues,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,5 @@ export { default as AddCredentialInLoadBalancing } from './add-credential-in-loa
|
||||||
export { default as AddCustomModel } from './add-custom-model'
|
export { default as AddCustomModel } from './add-custom-model'
|
||||||
export { default as ConfigProvider } from './config-provider'
|
export { default as ConfigProvider } from './config-provider'
|
||||||
export { default as ConfigModel } from './config-model'
|
export { default as ConfigModel } from './config-model'
|
||||||
|
export { default as ManageCustomModelCredentials } from './manage-custom-model-credentials'
|
||||||
|
export { default as CredentialSelector } from './credential-selector'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
} from '@/app/components/base/button'
|
||||||
|
import type {
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
|
ModelProvider,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
|
ModelModalModeEnum,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import Authorized from './authorized'
|
||||||
|
import {
|
||||||
|
useCustomModels,
|
||||||
|
} from './hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type ManageCustomModelCredentialsProps = {
|
||||||
|
provider: ModelProvider,
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
}
|
||||||
|
const ManageCustomModelCredentials = ({
|
||||||
|
provider,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
}: ManageCustomModelCredentialsProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const customModels = useCustomModels(provider)
|
||||||
|
const noModels = !customModels.length
|
||||||
|
|
||||||
|
const renderTrigger = useCallback((open?: boolean) => {
|
||||||
|
const Item = (
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
size='small'
|
||||||
|
className={cn(
|
||||||
|
'mr-0.5 text-text-tertiary',
|
||||||
|
open && 'bg-components-button-ghost-bg-hover',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('common.modelProvider.auth.manageCredentials')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
return Item
|
||||||
|
}, [t])
|
||||||
|
|
||||||
|
if (noModels)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
currentCustomConfigurationModelFixedFields={currentCustomConfigurationModelFixedFields}
|
||||||
|
items={customModels.map(model => ({
|
||||||
|
model,
|
||||||
|
credentials: model.available_model_credentials ?? [],
|
||||||
|
selectedCredential: model.current_credential_id ? {
|
||||||
|
credential_id: model.current_credential_id,
|
||||||
|
credential_name: model.current_credential_name,
|
||||||
|
} : undefined,
|
||||||
|
}))}
|
||||||
|
renderTrigger={renderTrigger}
|
||||||
|
authParams={{
|
||||||
|
isModelCredential: true,
|
||||||
|
mode: ModelModalModeEnum.configModelCredential,
|
||||||
|
}}
|
||||||
|
hideAddAction
|
||||||
|
disableItemClick
|
||||||
|
popupTitle={t('common.modelProvider.auth.customModelCredentials')}
|
||||||
|
showModelTitle
|
||||||
|
disableDeleteButShowAction
|
||||||
|
disableDeleteTip={t('common.modelProvider.auth.customModelCredentialsDeleteTip')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ManageCustomModelCredentials)
|
||||||
|
|
@ -13,7 +13,7 @@ import type {
|
||||||
CustomModel,
|
CustomModel,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { ConfigurationMethodEnum, ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import Badge from '@/app/components/base/badge'
|
import Badge from '@/app/components/base/badge'
|
||||||
|
|
@ -24,6 +24,8 @@ type SwitchCredentialInLoadBalancingProps = {
|
||||||
credentials?: Credential[]
|
credentials?: Credential[]
|
||||||
customModelCredential?: Credential
|
customModelCredential?: Credential
|
||||||
setCustomModelCredential: Dispatch<SetStateAction<Credential | undefined>>
|
setCustomModelCredential: Dispatch<SetStateAction<Credential | undefined>>
|
||||||
|
onUpdate?: (payload?: any, formValues?: Record<string, any>) => void
|
||||||
|
onRemove?: (credentialId: string) => void
|
||||||
}
|
}
|
||||||
const SwitchCredentialInLoadBalancing = ({
|
const SwitchCredentialInLoadBalancing = ({
|
||||||
provider,
|
provider,
|
||||||
|
|
@ -31,6 +33,8 @@ const SwitchCredentialInLoadBalancing = ({
|
||||||
customModelCredential,
|
customModelCredential,
|
||||||
setCustomModelCredential,
|
setCustomModelCredential,
|
||||||
credentials,
|
credentials,
|
||||||
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
}: SwitchCredentialInLoadBalancingProps) => {
|
}: SwitchCredentialInLoadBalancingProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
@ -94,27 +98,31 @@ const SwitchCredentialInLoadBalancing = ({
|
||||||
<Authorized
|
<Authorized
|
||||||
provider={provider}
|
provider={provider}
|
||||||
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
currentCustomConfigurationModelFixedFields={model ? {
|
||||||
|
__model_name: model.model,
|
||||||
|
__model_type: model.model_type,
|
||||||
|
} : undefined}
|
||||||
|
authParams={{
|
||||||
|
isModelCredential: true,
|
||||||
|
mode: ModelModalModeEnum.configModelCredential,
|
||||||
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
|
}}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
title: t('common.modelProvider.auth.modelCredentials'),
|
|
||||||
model,
|
model,
|
||||||
credentials: credentials || [],
|
credentials: credentials || [],
|
||||||
|
selectedCredential: customModelCredential ? {
|
||||||
|
credential_id: customModelCredential?.credential_id || '',
|
||||||
|
credential_name: customModelCredential?.credential_name || '',
|
||||||
|
} : undefined,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
renderTrigger={renderTrigger}
|
renderTrigger={renderTrigger}
|
||||||
onItemClick={handleItemClick}
|
onItemClick={handleItemClick}
|
||||||
isModelCredential
|
|
||||||
enableAddModelCredential
|
enableAddModelCredential
|
||||||
bottomAddModelCredentialText={t('common.modelProvider.auth.addModelCredential')}
|
|
||||||
selectedCredential={
|
|
||||||
customModelCredential
|
|
||||||
? {
|
|
||||||
credential_id: customModelCredential?.credential_id || '',
|
|
||||||
credential_name: customModelCredential?.credential_name || '',
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
showItemSelectedIcon
|
showItemSelectedIcon
|
||||||
|
popupTitle={t('common.modelProvider.auth.modelCredentials')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { RiCloseLine } from '@remixicon/react'
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
@ -15,6 +16,7 @@ import type {
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
FormTypeEnum,
|
FormTypeEnum,
|
||||||
|
ModelModalModeEnum,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import {
|
import {
|
||||||
useLanguage,
|
useLanguage,
|
||||||
|
|
@ -46,16 +48,19 @@ import {
|
||||||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||||
import Badge from '@/app/components/base/badge'
|
import Badge from '@/app/components/base/badge'
|
||||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
|
import { CredentialSelector } from '../model-auth'
|
||||||
|
|
||||||
type ModelModalProps = {
|
type ModelModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
configurateMethod: ConfigurationMethodEnum
|
configurateMethod: ConfigurationMethodEnum
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onSave: () => void
|
onSave: (formValues?: Record<string, any>) => void
|
||||||
|
onRemove: (formValues?: Record<string, any>) => void
|
||||||
model?: CustomModel
|
model?: CustomModel
|
||||||
credential?: Credential
|
credential?: Credential
|
||||||
isModelCredential?: boolean
|
isModelCredential?: boolean
|
||||||
|
mode?: ModelModalModeEnum
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModelModal: FC<ModelModalProps> = ({
|
const ModelModal: FC<ModelModalProps> = ({
|
||||||
|
|
@ -67,6 +72,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
model,
|
model,
|
||||||
credential,
|
credential,
|
||||||
isModelCredential,
|
isModelCredential,
|
||||||
|
mode = ModelModalModeEnum.configProviderCredential,
|
||||||
}) => {
|
}) => {
|
||||||
const renderI18nObject = useRenderI18nObject()
|
const renderI18nObject = useRenderI18nObject()
|
||||||
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
||||||
|
|
@ -81,40 +87,88 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
closeConfirmDelete,
|
closeConfirmDelete,
|
||||||
openConfirmDelete,
|
openConfirmDelete,
|
||||||
doingAction,
|
doingAction,
|
||||||
} = useAuth(provider, configurateMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onSave)
|
handleActiveCredential,
|
||||||
|
} = useAuth(
|
||||||
|
provider,
|
||||||
|
configurateMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
{
|
||||||
|
isModelCredential,
|
||||||
|
mode,
|
||||||
|
},
|
||||||
|
)
|
||||||
const {
|
const {
|
||||||
credentials: formSchemasValue,
|
credentials: formSchemasValue,
|
||||||
|
available_credentials,
|
||||||
} = credentialData as any
|
} = credentialData as any
|
||||||
|
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const {
|
const {
|
||||||
formSchemas,
|
formSchemas,
|
||||||
formValues,
|
formValues,
|
||||||
|
modelNameAndTypeFormSchemas,
|
||||||
|
modelNameAndTypeFormValues,
|
||||||
} = useModelFormSchemas(provider, providerFormSchemaPredefined, formSchemasValue, credential, model)
|
} = 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 () => {
|
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 {
|
const {
|
||||||
isCheckValidated,
|
isCheckValidated,
|
||||||
values,
|
values,
|
||||||
} = formRef.current?.getFormValues({
|
} = formRef2.current?.getFormValues({
|
||||||
needCheckValidatedValues: true,
|
needCheckValidatedValues: true,
|
||||||
needTransformWhenSecretFieldIsPristine: true,
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
}) || { isCheckValidated: false, values: {} }
|
}) || { isCheckValidated: false, values: {} }
|
||||||
if (!isCheckValidated)
|
if (!isCheckValidated || !modelNameAndTypeIsCheckValidated)
|
||||||
return
|
return
|
||||||
|
|
||||||
const {
|
const {
|
||||||
__authorization_name__,
|
|
||||||
__model_name,
|
__model_name,
|
||||||
__model_type,
|
__model_type,
|
||||||
|
} = modelNameAndTypeValues
|
||||||
|
const {
|
||||||
|
__authorization_name__,
|
||||||
...rest
|
...rest
|
||||||
} = values
|
} = values
|
||||||
if (__model_name && __model_type) {
|
if (__model_name && __model_type && __authorization_name__) {
|
||||||
handleSaveCredential({
|
await handleSaveCredential({
|
||||||
credential_id: credential?.credential_id,
|
credential_id: credential?.credential_id,
|
||||||
credentials: rest,
|
credentials: rest,
|
||||||
name: __authorization_name__,
|
name: __authorization_name__,
|
||||||
|
|
@ -123,41 +177,33 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handleSaveCredential({
|
await handleSaveCredential({
|
||||||
credential_id: credential?.credential_id,
|
credential_id: credential?.credential_id,
|
||||||
credentials: rest,
|
credentials: rest,
|
||||||
name: __authorization_name__,
|
name: __authorization_name__,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [handleSaveCredential, credential?.credential_id, model])
|
onSave(values)
|
||||||
|
}, [handleSaveCredential, credential?.credential_id, model, onSave, mode, selectedCredential, handleActiveCredential])
|
||||||
|
|
||||||
const modalTitle = useMemo(() => {
|
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')
|
let label = t('common.modelProvider.auth.apiKeyModal.title')
|
||||||
|
|
||||||
if (model)
|
if (mode === ModelModalModeEnum.configCustomModel || mode === ModelModalModeEnum.addCustomModelToModelList)
|
||||||
label = t('common.modelProvider.auth.addModelCredential')
|
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 (
|
return (
|
||||||
<div className='title-2xl-semi-bold text-text-primary'>
|
<div className='title-2xl-semi-bold text-text-primary'>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}, [providerFormSchemaPredefined, t, model, renderI18nObject])
|
}, [t, mode, credential])
|
||||||
|
|
||||||
const modalDesc = useMemo(() => {
|
const modalDesc = useMemo(() => {
|
||||||
if (providerFormSchemaPredefined) {
|
if (providerFormSchemaPredefined) {
|
||||||
|
|
@ -172,7 +218,18 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
}, [providerFormSchemaPredefined, t])
|
}, [providerFormSchemaPredefined, t])
|
||||||
|
|
||||||
const modalModel = useMemo(() => {
|
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 (
|
return (
|
||||||
<div className='mt-2 flex items-center'>
|
<div className='mt-2 flex items-center'>
|
||||||
<ModelIcon
|
<ModelIcon
|
||||||
|
|
@ -187,7 +244,38 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
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(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
|
@ -214,100 +302,132 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
>
|
>
|
||||||
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
|
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
<div className='px-6 pt-6'>
|
<div className='p-6 pb-3'>
|
||||||
<div className='pb-3'>
|
{modalTitle}
|
||||||
{modalTitle}
|
{modalDesc}
|
||||||
{modalDesc}
|
{modalModel}
|
||||||
{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>
|
</div>
|
||||||
<div className='border-t-[0.5px] border-t-divider-regular'>
|
<div className='max-h-[calc(100vh-320px)] overflow-y-auto px-6 py-3'>
|
||||||
<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' />
|
mode === ModelModalModeEnum.configCustomModel && (
|
||||||
{t('common.modelProvider.encrypted.front')}
|
<AuthForm
|
||||||
<a
|
formSchemas={modelNameAndTypeFormSchemas.map((formSchema) => {
|
||||||
className='mx-1 text-text-accent'
|
return {
|
||||||
target='_blank' rel='noopener noreferrer'
|
...formSchema,
|
||||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
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
|
{t('common.operation.cancel')}
|
||||||
</a>
|
</Button>
|
||||||
{t('common.modelProvider.encrypted.back')}
|
<Button
|
||||||
|
variant='primary'
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={isLoading || doingAction}
|
||||||
|
>
|
||||||
|
{saveButtonText}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
{
|
{
|
||||||
deleteCredentialId && (
|
deleteCredentialId && (
|
||||||
|
|
@ -316,7 +436,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
||||||
title={t('common.modelProvider.confirmDelete')}
|
title={t('common.modelProvider.confirmDelete')}
|
||||||
isDisabled={doingAction}
|
isDisabled={doingAction}
|
||||||
onCancel={closeConfirmDelete}
|
onCancel={closeConfirmDelete}
|
||||||
onConfirm={handleConfirmDelete}
|
onConfirm={handleDeleteCredential}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ const CredentialPanel = ({
|
||||||
<div className='flex items-center gap-0.5'>
|
<div className='flex items-center gap-0.5'>
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
provider={provider}
|
provider={provider}
|
||||||
configurationMethod={ConfigurationMethodEnum.predefinedModel}
|
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
systemConfig.enabled && isCustomConfigured && (
|
systemConfig.enabled && isCustomConfigured && (
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,10 @@ import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { AddCustomModel } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
import {
|
||||||
|
AddCustomModel,
|
||||||
|
ManageCustomModelCredentials,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
|
||||||
export const UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST = 'UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST'
|
export const UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST = 'UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST'
|
||||||
type ProviderAddedCardProps = {
|
type ProviderAddedCardProps = {
|
||||||
|
|
@ -155,10 +158,17 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
||||||
)}
|
)}
|
||||||
{
|
{
|
||||||
configurationMethods.includes(ConfigurationMethodEnum.customizableModel) && isCurrentWorkspaceManager && (
|
configurationMethods.includes(ConfigurationMethodEnum.customizableModel) && isCurrentWorkspaceManager && (
|
||||||
<AddCustomModel
|
<div className='flex grow justify-end'>
|
||||||
provider={provider}
|
<ManageCustomModelCredentials
|
||||||
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
provider={provider}
|
||||||
/>
|
currentCustomConfigurationModelFixedFields={undefined}
|
||||||
|
/>
|
||||||
|
<AddCustomModel
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
currentCustomConfigurationModelFixedFields={undefined}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ import {
|
||||||
import ModelListItem from './model-list-item'
|
import ModelListItem from './model-list-item'
|
||||||
import { useModalContextSelector } from '@/context/modal-context'
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { AddCustomModel } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
import {
|
||||||
|
AddCustomModel,
|
||||||
|
ManageCustomModelCredentials,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
|
||||||
type ModelListProps = {
|
type ModelListProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
|
|
@ -67,6 +70,10 @@ const ModelList: FC<ModelListProps> = ({
|
||||||
{
|
{
|
||||||
isConfigurable && isCurrentWorkspaceManager && (
|
isConfigurable && isCurrentWorkspaceManager && (
|
||||||
<div className='flex grow justify-end'>
|
<div className='flex grow justify-end'>
|
||||||
|
<ManageCustomModelCredentials
|
||||||
|
provider={provider}
|
||||||
|
currentCustomConfigurationModelFixedFields={undefined}
|
||||||
|
/>
|
||||||
<AddCustomModel
|
<AddCustomModel
|
||||||
provider={provider}
|
provider={provider}
|
||||||
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ import type { Dispatch, SetStateAction } from 'react'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
RiDeleteBinLine,
|
RiIndeterminateCircleLine,
|
||||||
RiEqualizer2Line,
|
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import type {
|
import type {
|
||||||
Credential,
|
Credential,
|
||||||
|
|
@ -28,7 +27,6 @@ import GridMask from '@/app/components/base/grid-mask'
|
||||||
import { useProviderContextSelector } from '@/context/provider-context'
|
import { useProviderContextSelector } from '@/context/provider-context'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
import { AddCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
import { AddCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
import { useModelModalHandler } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
|
||||||
import Badge from '@/app/components/base/badge/index'
|
import Badge from '@/app/components/base/badge/index'
|
||||||
|
|
||||||
export type ModelLoadBalancingConfigsProps = {
|
export type ModelLoadBalancingConfigsProps = {
|
||||||
|
|
@ -40,7 +38,8 @@ export type ModelLoadBalancingConfigsProps = {
|
||||||
withSwitch?: boolean
|
withSwitch?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
modelCredential: ModelCredential
|
modelCredential: ModelCredential
|
||||||
onUpdate?: () => void
|
onUpdate?: (payload?: any, formValues?: Record<string, any>) => void
|
||||||
|
onRemove?: (credentialId: string) => void
|
||||||
model: CustomModelCredential
|
model: CustomModelCredential
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,11 +54,11 @@ const ModelLoadBalancingConfigs = ({
|
||||||
className,
|
className,
|
||||||
modelCredential,
|
modelCredential,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
|
onRemove,
|
||||||
}: ModelLoadBalancingConfigsProps) => {
|
}: ModelLoadBalancingConfigsProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const providerFormSchemaPredefined = configurationMethod === ConfigurationMethodEnum.predefinedModel
|
const providerFormSchemaPredefined = configurationMethod === ConfigurationMethodEnum.predefinedModel
|
||||||
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
||||||
const handleOpenModal = useModelModalHandler()
|
|
||||||
|
|
||||||
const updateConfigEntry = useCallback(
|
const updateConfigEntry = useCallback(
|
||||||
(
|
(
|
||||||
|
|
@ -130,6 +129,17 @@ const ModelLoadBalancingConfigs = ({
|
||||||
return draftConfig.configs
|
return draftConfig.configs
|
||||||
}, [draftConfig])
|
}, [draftConfig])
|
||||||
|
|
||||||
|
const handleUpdate = useCallback((payload?: any, formValues?: Record<string, any>) => {
|
||||||
|
onUpdate?.(payload, formValues)
|
||||||
|
}, [onUpdate])
|
||||||
|
|
||||||
|
const handleRemove = useCallback((credentialId: string) => {
|
||||||
|
const index = draftConfig?.configs.findIndex(item => item.credential_id === credentialId && item.name !== '__inherit__')
|
||||||
|
if (index && index > -1)
|
||||||
|
updateConfigEntry(index, () => undefined)
|
||||||
|
onRemove?.(credentialId)
|
||||||
|
}, [draftConfig?.configs, updateConfigEntry, onRemove])
|
||||||
|
|
||||||
if (!draftConfig)
|
if (!draftConfig)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
@ -190,7 +200,7 @@ const ModelLoadBalancingConfigs = ({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className='mr-1 text-[13px]'>
|
<div className='mr-1 text-[13px] text-text-secondary'>
|
||||||
{isProviderManaged ? t('common.modelProvider.defaultConfig') : config.name}
|
{isProviderManaged ? t('common.modelProvider.defaultConfig') : config.name}
|
||||||
</div>
|
</div>
|
||||||
{isProviderManaged && providerFormSchemaPredefined && (
|
{isProviderManaged && providerFormSchemaPredefined && (
|
||||||
|
|
@ -206,34 +216,14 @@ const ModelLoadBalancingConfigs = ({
|
||||||
{!isProviderManaged && (
|
{!isProviderManaged && (
|
||||||
<>
|
<>
|
||||||
<div className='flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100'>
|
<div className='flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100'>
|
||||||
{
|
<Tooltip popupContent={t('common.operation.remove')}>
|
||||||
config.credential_id && !credential?.not_allowed_to_use && !credential?.from_enterprise && (
|
<span
|
||||||
<span
|
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
||||||
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
onClick={() => updateConfigEntry(index, () => undefined)}
|
||||||
onClick={() => {
|
>
|
||||||
handleOpenModal(
|
<RiIndeterminateCircleLine className='h-4 w-4' />
|
||||||
provider,
|
</span>
|
||||||
configurationMethod,
|
</Tooltip>
|
||||||
currentCustomConfigurationModelFixedFields,
|
|
||||||
configurationMethod === ConfigurationMethodEnum.customizableModel,
|
|
||||||
(config.credential_id && config.name) ? {
|
|
||||||
credential_id: config.credential_id,
|
|
||||||
credential_name: config.name,
|
|
||||||
} : undefined,
|
|
||||||
model,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RiEqualizer2Line className='h-4 w-4' />
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<span
|
|
||||||
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
|
||||||
onClick={() => updateConfigEntry(index, () => undefined)}
|
|
||||||
>
|
|
||||||
<RiDeleteBinLine className='h-4 w-4' />
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -261,7 +251,8 @@ const ModelLoadBalancingConfigs = ({
|
||||||
configurationMethod={configurationMethod}
|
configurationMethod={configurationMethod}
|
||||||
modelCredential={modelCredential}
|
modelCredential={modelCredential}
|
||||||
onSelectCredential={addConfigEntry}
|
onSelectCredential={addConfigEntry}
|
||||||
onUpdate={onUpdate}
|
onUpdate={handleUpdate}
|
||||||
|
onRemove={handleRemove}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type {
|
import type {
|
||||||
Credential,
|
Credential,
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
ModelItem,
|
ModelItem,
|
||||||
ModelLoadBalancingConfig,
|
ModelLoadBalancingConfig,
|
||||||
ModelLoadBalancingConfigEntry,
|
ModelLoadBalancingConfigEntry,
|
||||||
|
|
@ -24,10 +25,14 @@ import {
|
||||||
useGetModelCredential,
|
useGetModelCredential,
|
||||||
useUpdateModelLoadBalancingConfig,
|
useUpdateModelLoadBalancingConfig,
|
||||||
} from '@/service/use-models'
|
} from '@/service/use-models'
|
||||||
|
import { useAuth } from '../model-auth/hooks/use-auth'
|
||||||
|
import Confirm from '@/app/components/base/confirm'
|
||||||
|
import { useRefreshModel } from '../hooks'
|
||||||
|
|
||||||
export type ModelLoadBalancingModalProps = {
|
export type ModelLoadBalancingModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
configurateMethod: ConfigurationMethodEnum
|
configurateMethod: ConfigurationMethodEnum
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
model: ModelItem
|
model: ModelItem
|
||||||
credential?: Credential
|
credential?: Credential
|
||||||
open?: boolean
|
open?: boolean
|
||||||
|
|
@ -39,6 +44,7 @@ export type ModelLoadBalancingModalProps = {
|
||||||
const ModelLoadBalancingModal = ({
|
const ModelLoadBalancingModal = ({
|
||||||
provider,
|
provider,
|
||||||
configurateMethod,
|
configurateMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
model,
|
model,
|
||||||
credential,
|
credential,
|
||||||
open = false,
|
open = false,
|
||||||
|
|
@ -47,7 +53,20 @@ const ModelLoadBalancingModal = ({
|
||||||
}: ModelLoadBalancingModalProps) => {
|
}: ModelLoadBalancingModalProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
|
const {
|
||||||
|
doingAction,
|
||||||
|
deleteModel,
|
||||||
|
openConfirmDelete,
|
||||||
|
closeConfirmDelete,
|
||||||
|
handleConfirmDelete,
|
||||||
|
} = useAuth(
|
||||||
|
provider,
|
||||||
|
configurateMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
{
|
||||||
|
isModelCredential: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
||||||
const configFrom = providerFormSchemaPredefined ? 'predefined-model' : 'custom-model'
|
const configFrom = providerFormSchemaPredefined ? 'predefined-model' : 'custom-model'
|
||||||
|
|
@ -121,6 +140,7 @@ const ModelLoadBalancingModal = ({
|
||||||
}
|
}
|
||||||
}, [current_credential_id, current_credential_name])
|
}, [current_credential_id, current_credential_name])
|
||||||
const [customModelCredential, setCustomModelCredential] = useState<Credential | undefined>(initialCustomModelCredential)
|
const [customModelCredential, setCustomModelCredential] = useState<Credential | undefined>(initialCustomModelCredential)
|
||||||
|
const { handleRefreshModel } = useRefreshModel()
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
@ -139,6 +159,7 @@ const ModelLoadBalancingModal = ({
|
||||||
)
|
)
|
||||||
if (res.result === 'success') {
|
if (res.result === 'success') {
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
|
handleRefreshModel(provider, configurateMethod, currentCustomConfigurationModelFixedFields)
|
||||||
onSave?.(provider.provider)
|
onSave?.(provider.provider)
|
||||||
onClose?.()
|
onClose?.()
|
||||||
}
|
}
|
||||||
|
|
@ -147,120 +168,208 @@ const ModelLoadBalancingModal = ({
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const handleDeleteModel = useCallback(async () => {
|
||||||
|
await handleConfirmDelete()
|
||||||
|
onClose?.()
|
||||||
|
}, [handleConfirmDelete, onClose])
|
||||||
|
|
||||||
|
const handleUpdate = useCallback(async (payload?: any, formValues?: Record<string, any>) => {
|
||||||
|
const result = await refetch()
|
||||||
|
const available_credentials = result.data?.available_credentials || []
|
||||||
|
const credentialName = formValues?.__authorization_name__
|
||||||
|
const modelCredential = payload?.credential
|
||||||
|
|
||||||
|
if (!available_credentials.length) {
|
||||||
|
onClose?.()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modelCredential) {
|
||||||
|
const currentCredential = available_credentials.find(c => c.credential_name === credentialName)
|
||||||
|
if (currentCredential) {
|
||||||
|
setDraftConfig((prev: any) => {
|
||||||
|
if (!prev)
|
||||||
|
return prev
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
configs: [...prev.configs, {
|
||||||
|
credential_id: currentCredential.credential_id,
|
||||||
|
enabled: true,
|
||||||
|
name: currentCredential.credential_name,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setDraftConfig((prev) => {
|
||||||
|
if (!prev)
|
||||||
|
return prev
|
||||||
|
const newConfigs = [...prev.configs]
|
||||||
|
const prevIndex = newConfigs.findIndex(item => item.credential_id === modelCredential.credential_id && item.name !== '__inherit__')
|
||||||
|
const newIndex = available_credentials.findIndex(c => c.credential_id === modelCredential.credential_id)
|
||||||
|
|
||||||
|
if (newIndex > -1 && prevIndex > -1)
|
||||||
|
newConfigs[prevIndex].name = available_credentials[newIndex].credential_name || ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
configs: newConfigs,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [refetch, credential])
|
||||||
|
|
||||||
|
const handleUpdateWhenSwitchCredential = useCallback(async () => {
|
||||||
|
const result = await refetch()
|
||||||
|
const available_credentials = result.data?.available_credentials || []
|
||||||
|
if (!available_credentials.length)
|
||||||
|
onClose?.()
|
||||||
|
}, [refetch, onClose])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<>
|
||||||
isShow={Boolean(model) && open}
|
<Modal
|
||||||
onClose={onClose}
|
isShow={Boolean(model) && open}
|
||||||
className='w-[640px] max-w-none px-8 pt-8'
|
onClose={onClose}
|
||||||
title={
|
className='w-[640px] max-w-none px-8 pt-8'
|
||||||
<div className='pb-3 font-semibold'>
|
title={
|
||||||
<div className='h-[30px]'>{
|
<div className='pb-3 font-semibold'>
|
||||||
draftConfig?.enabled
|
<div className='h-[30px]'>{
|
||||||
? t('common.modelProvider.auth.configLoadBalancing')
|
draftConfig?.enabled
|
||||||
: t('common.modelProvider.auth.configModel')
|
? t('common.modelProvider.auth.configLoadBalancing')
|
||||||
}</div>
|
: t('common.modelProvider.auth.configModel')
|
||||||
{Boolean(model) && (
|
}</div>
|
||||||
<div className='flex h-5 items-center'>
|
{Boolean(model) && (
|
||||||
<ModelIcon
|
<div className='flex h-5 items-center'>
|
||||||
className='mr-2 shrink-0'
|
<ModelIcon
|
||||||
provider={provider}
|
className='mr-2 shrink-0'
|
||||||
modelName={model!.model}
|
provider={provider}
|
||||||
/>
|
modelName={model!.model}
|
||||||
<ModelName
|
/>
|
||||||
className='system-md-regular grow text-text-secondary'
|
<ModelName
|
||||||
modelItem={model!}
|
className='system-md-regular grow text-text-secondary'
|
||||||
showModelType
|
modelItem={model!}
|
||||||
showMode
|
showModelType
|
||||||
showContextSize
|
showMode
|
||||||
/>
|
showContextSize
|
||||||
</div>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
}
|
</div>
|
||||||
>
|
}
|
||||||
{!draftConfig
|
>
|
||||||
? <Loading type='area' />
|
{!draftConfig
|
||||||
: (
|
? <Loading type='area' />
|
||||||
<>
|
: (
|
||||||
<div className='py-2'>
|
<>
|
||||||
<div
|
<div className='py-2'>
|
||||||
className={classNames(
|
<div
|
||||||
'min-h-16 rounded-xl border bg-components-panel-bg transition-colors',
|
className={classNames(
|
||||||
draftConfig.enabled ? 'cursor-pointer border-components-panel-border' : 'cursor-default border-util-colors-blue-blue-600',
|
'min-h-16 rounded-xl border bg-components-panel-bg transition-colors',
|
||||||
)}
|
draftConfig.enabled ? 'cursor-pointer border-components-panel-border' : 'cursor-default border-util-colors-blue-blue-600',
|
||||||
onClick={draftConfig.enabled ? () => toggleModalBalancing(false) : undefined}
|
)}
|
||||||
>
|
onClick={draftConfig.enabled ? () => toggleModalBalancing(false) : undefined}
|
||||||
<div className='flex select-none items-center gap-2 px-[15px] py-3'>
|
>
|
||||||
<div className='flex h-8 w-8 shrink-0 grow-0 items-center justify-center rounded-lg border border-components-card-border bg-components-card-bg'>
|
<div className='flex select-none items-center gap-2 px-[15px] py-3'>
|
||||||
{Boolean(model) && (
|
<div className='flex h-8 w-8 shrink-0 grow-0 items-center justify-center rounded-lg border border-components-card-border bg-components-card-bg'>
|
||||||
<ModelIcon className='shrink-0' provider={provider} modelName={model!.model} />
|
{Boolean(model) && (
|
||||||
)}
|
<ModelIcon className='shrink-0' provider={provider} modelName={model!.model} />
|
||||||
</div>
|
)}
|
||||||
<div className='grow'>
|
</div>
|
||||||
<div className='text-sm text-text-secondary'>{
|
<div className='grow'>
|
||||||
providerFormSchemaPredefined
|
<div className='text-sm text-text-secondary'>{
|
||||||
? t('common.modelProvider.auth.providerManaged')
|
providerFormSchemaPredefined
|
||||||
: t('common.modelProvider.auth.specifyModelCredential')
|
? t('common.modelProvider.auth.providerManaged')
|
||||||
}</div>
|
: t('common.modelProvider.auth.specifyModelCredential')
|
||||||
<div className='text-xs text-text-tertiary'>{
|
}</div>
|
||||||
providerFormSchemaPredefined
|
<div className='text-xs text-text-tertiary'>{
|
||||||
? t('common.modelProvider.auth.providerManagedTip')
|
providerFormSchemaPredefined
|
||||||
: t('common.modelProvider.auth.specifyModelCredentialTip')
|
? t('common.modelProvider.auth.providerManagedTip')
|
||||||
}</div>
|
: t('common.modelProvider.auth.specifyModelCredentialTip')
|
||||||
|
}</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
!providerFormSchemaPredefined && (
|
||||||
|
<SwitchCredentialInLoadBalancing
|
||||||
|
provider={provider}
|
||||||
|
customModelCredential={customModelCredential ?? initialCustomModelCredential}
|
||||||
|
setCustomModelCredential={setCustomModelCredential}
|
||||||
|
model={model}
|
||||||
|
credentials={available_credentials}
|
||||||
|
onUpdate={handleUpdateWhenSwitchCredential}
|
||||||
|
onRemove={handleUpdateWhenSwitchCredential}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
modelCredential && (
|
||||||
|
<ModelLoadBalancingConfigs {...{
|
||||||
|
draftConfig,
|
||||||
|
setDraftConfig,
|
||||||
|
provider,
|
||||||
|
currentCustomConfigurationModelFixedFields: {
|
||||||
|
__model_name: model.model,
|
||||||
|
__model_type: model.model_type,
|
||||||
|
},
|
||||||
|
configurationMethod: model.fetch_from,
|
||||||
|
className: 'mt-2',
|
||||||
|
modelCredential,
|
||||||
|
onUpdate: handleUpdate,
|
||||||
|
onRemove: handleUpdateWhenSwitchCredential,
|
||||||
|
model: {
|
||||||
|
model: model.model,
|
||||||
|
model_type: model.model_type,
|
||||||
|
},
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6 flex items-center justify-between gap-2'>
|
||||||
|
<div>
|
||||||
{
|
{
|
||||||
!providerFormSchemaPredefined && (
|
!providerFormSchemaPredefined && (
|
||||||
<SwitchCredentialInLoadBalancing
|
<Button
|
||||||
provider={provider}
|
onClick={() => openConfirmDelete(undefined, { model: model.model, model_type: model.model_type })}
|
||||||
customModelCredential={initialCustomModelCredential ?? customModelCredential}
|
className='text-components-button-destructive-secondary-text'
|
||||||
setCustomModelCredential={setCustomModelCredential}
|
>
|
||||||
model={model}
|
{t('common.modelProvider.auth.removeModel')}
|
||||||
credentials={available_credentials}
|
</Button>
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<div className='space-x-2'>
|
||||||
|
<Button onClick={onClose}>{t('common.operation.cancel')}</Button>
|
||||||
|
<Button
|
||||||
|
variant='primary'
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={
|
||||||
|
loading
|
||||||
|
|| (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
|
||||||
|
|| isLoading
|
||||||
|
}
|
||||||
|
>{t('common.operation.save')}</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
</>
|
||||||
modelCredential && (
|
)
|
||||||
<ModelLoadBalancingConfigs {...{
|
}
|
||||||
draftConfig,
|
</Modal >
|
||||||
setDraftConfig,
|
{
|
||||||
provider,
|
deleteModel && (
|
||||||
currentCustomConfigurationModelFixedFields: {
|
<Confirm
|
||||||
__model_name: model.model,
|
isShow
|
||||||
__model_type: model.model_type,
|
title={t('common.modelProvider.confirmDelete')}
|
||||||
},
|
onCancel={closeConfirmDelete}
|
||||||
configurationMethod: model.fetch_from,
|
onConfirm={handleDeleteModel}
|
||||||
className: 'mt-2',
|
isDisabled={doingAction}
|
||||||
modelCredential,
|
/>
|
||||||
onUpdate: refetch,
|
|
||||||
model: {
|
|
||||||
model: model.model,
|
|
||||||
model_type: model.model_type,
|
|
||||||
},
|
|
||||||
}} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6 flex items-center justify-end gap-2'>
|
|
||||||
<Button onClick={onClose}>{t('common.operation.cancel')}</Button>
|
|
||||||
<Button
|
|
||||||
variant='primary'
|
|
||||||
onClick={handleSave}
|
|
||||||
disabled={
|
|
||||||
loading
|
|
||||||
|| (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
|
|
||||||
|| isLoading
|
|
||||||
}
|
|
||||||
>{t('common.operation.save')}</Button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</Modal >
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ export const modelTypeFormat = (modelType: ModelTypeEnum) => {
|
||||||
|
|
||||||
export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => {
|
export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => {
|
||||||
return {
|
return {
|
||||||
type: FormTypeEnum.radio,
|
type: FormTypeEnum.select,
|
||||||
label: {
|
label: {
|
||||||
zh_Hans: '模型类型',
|
zh_Hans: '模型类型',
|
||||||
en_US: 'Model Type',
|
en_US: 'Model Type',
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import type {
|
||||||
Credential,
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
CustomModel,
|
CustomModel,
|
||||||
ModelLoadBalancingConfigEntry,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import {
|
import {
|
||||||
|
|
@ -29,6 +28,7 @@ import { removeSpecificQueryParam } from '@/utils'
|
||||||
import { noop } from 'lodash-es'
|
import { noop } from 'lodash-es'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal'
|
import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal'
|
||||||
|
import type { ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
|
||||||
const AccountSetting = dynamic(() => import('@/app/components/header/account-setting'), {
|
const AccountSetting = dynamic(() => import('@/app/components/header/account-setting'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
|
|
@ -71,8 +71,8 @@ const ExpireNoticeModal = dynamic(() => import('@/app/education-apply/expire-not
|
||||||
export type ModalState<T> = {
|
export type ModalState<T> = {
|
||||||
payload: T
|
payload: T
|
||||||
onCancelCallback?: () => void
|
onCancelCallback?: () => void
|
||||||
onSaveCallback?: (newPayload: T) => void
|
onSaveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
|
||||||
onRemoveCallback?: (newPayload: T) => void
|
onRemoveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
|
||||||
onEditCallback?: (newPayload: T) => void
|
onEditCallback?: (newPayload: T) => void
|
||||||
onValidateBeforeSaveCallback?: (newPayload: T) => boolean
|
onValidateBeforeSaveCallback?: (newPayload: T) => boolean
|
||||||
isEditMode?: boolean
|
isEditMode?: boolean
|
||||||
|
|
@ -86,10 +86,7 @@ export type ModelModalType = {
|
||||||
isModelCredential?: boolean
|
isModelCredential?: boolean
|
||||||
credential?: Credential
|
credential?: Credential
|
||||||
model?: CustomModel
|
model?: CustomModel
|
||||||
}
|
mode?: ModelModalModeEnum
|
||||||
export type LoadBalancingEntryModalType = ModelModalType & {
|
|
||||||
entry?: ModelLoadBalancingConfigEntry
|
|
||||||
index?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModalContextState = {
|
export type ModalContextState = {
|
||||||
|
|
@ -187,9 +184,15 @@ export const ModalContextProvider = ({
|
||||||
showModelModal.onCancelCallback()
|
showModelModal.onCancelCallback()
|
||||||
}, [showModelModal])
|
}, [showModelModal])
|
||||||
|
|
||||||
const handleSaveModelModal = useCallback(() => {
|
const handleSaveModelModal = useCallback((formValues?: Record<string, any>) => {
|
||||||
if (showModelModal?.onSaveCallback)
|
if (showModelModal?.onSaveCallback)
|
||||||
showModelModal.onSaveCallback(showModelModal.payload)
|
showModelModal.onSaveCallback(showModelModal.payload, formValues)
|
||||||
|
setShowModelModal(null)
|
||||||
|
}, [showModelModal])
|
||||||
|
|
||||||
|
const handleRemoveModelModal = useCallback((formValues?: Record<string, any>) => {
|
||||||
|
if (showModelModal?.onRemoveCallback)
|
||||||
|
showModelModal.onRemoveCallback(showModelModal.payload, formValues)
|
||||||
setShowModelModal(null)
|
setShowModelModal(null)
|
||||||
}, [showModelModal])
|
}, [showModelModal])
|
||||||
|
|
||||||
|
|
@ -329,8 +332,10 @@ export const ModalContextProvider = ({
|
||||||
isModelCredential={showModelModal.payload.isModelCredential}
|
isModelCredential={showModelModal.payload.isModelCredential}
|
||||||
credential={showModelModal.payload.credential}
|
credential={showModelModal.payload.credential}
|
||||||
model={showModelModal.payload.model}
|
model={showModelModal.payload.model}
|
||||||
|
mode={showModelModal.payload.mode}
|
||||||
onCancel={handleCancelModelModal}
|
onCancel={handleCancelModelModal}
|
||||||
onSave={handleSaveModelModal}
|
onSave={handleSaveModelModal}
|
||||||
|
onRemove={handleRemoveModelModal}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -498,10 +498,13 @@ const translation = {
|
||||||
authRemoved: 'Auth removed',
|
authRemoved: 'Auth removed',
|
||||||
apiKeys: 'API Keys',
|
apiKeys: 'API Keys',
|
||||||
addApiKey: 'Add API Key',
|
addApiKey: 'Add API Key',
|
||||||
|
addModel: 'Add model',
|
||||||
addNewModel: 'Add new model',
|
addNewModel: 'Add new model',
|
||||||
addCredential: 'Add credential',
|
addCredential: 'Add credential',
|
||||||
addModelCredential: 'Add model credential',
|
addModelCredential: 'Add model credential',
|
||||||
|
editModelCredential: 'Edit model credential',
|
||||||
modelCredentials: 'Model credentials',
|
modelCredentials: 'Model credentials',
|
||||||
|
modelCredential: 'Model credential',
|
||||||
configModel: 'Config model',
|
configModel: 'Config model',
|
||||||
configLoadBalancing: 'Config Load Balancing',
|
configLoadBalancing: 'Config Load Balancing',
|
||||||
authorizationError: 'Authorization error',
|
authorizationError: 'Authorization error',
|
||||||
|
|
@ -514,6 +517,12 @@ const translation = {
|
||||||
desc: 'After configuring credentials, all members within the workspace can use this model when orchestrating applications.',
|
desc: 'After configuring credentials, all members within the workspace can use this model when orchestrating applications.',
|
||||||
addModel: 'Add model',
|
addModel: 'Add model',
|
||||||
},
|
},
|
||||||
|
manageCredentials: 'Manage Credentials',
|
||||||
|
customModelCredentials: 'Custom Model Credentials',
|
||||||
|
addNewModelCredential: 'Add new model credential',
|
||||||
|
removeModel: 'Remove Model',
|
||||||
|
selectModelCredential: 'Select a model credential',
|
||||||
|
customModelCredentialsDeleteTip: 'Credential is in use and cannot be deleted',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
|
|
|
||||||
|
|
@ -492,10 +492,13 @@ const translation = {
|
||||||
authRemoved: '授权已移除',
|
authRemoved: '授权已移除',
|
||||||
apiKeys: 'API 密钥',
|
apiKeys: 'API 密钥',
|
||||||
addApiKey: '添加 API 密钥',
|
addApiKey: '添加 API 密钥',
|
||||||
|
addModel: '添加模型',
|
||||||
addNewModel: '添加新模型',
|
addNewModel: '添加新模型',
|
||||||
addCredential: '添加凭据',
|
addCredential: '添加凭据',
|
||||||
addModelCredential: '添加模型凭据',
|
addModelCredential: '添加模型凭据',
|
||||||
|
editModelCredential: '编辑模型凭据',
|
||||||
modelCredentials: '模型凭据',
|
modelCredentials: '模型凭据',
|
||||||
|
modelCredential: '模型凭据',
|
||||||
configModel: '配置模型',
|
configModel: '配置模型',
|
||||||
configLoadBalancing: '配置负载均衡',
|
configLoadBalancing: '配置负载均衡',
|
||||||
authorizationError: '授权错误',
|
authorizationError: '授权错误',
|
||||||
|
|
@ -508,6 +511,12 @@ const translation = {
|
||||||
desc: '配置凭据后,工作空间中的所有成员都可以在编排应用时使用此模型。',
|
desc: '配置凭据后,工作空间中的所有成员都可以在编排应用时使用此模型。',
|
||||||
addModel: '添加模型',
|
addModel: '添加模型',
|
||||||
},
|
},
|
||||||
|
manageCredentials: '管理凭据',
|
||||||
|
customModelCredentials: '自定义模型凭据',
|
||||||
|
addNewModelCredential: '添加模型新凭据',
|
||||||
|
removeModel: '移除模型',
|
||||||
|
selectModelCredential: '选择模型凭据',
|
||||||
|
customModelCredentialsDeleteTip: '模型凭据正在使用中,无法删除',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ export const useDeleteModel = (provider: string) => {
|
||||||
mutationFn: (data: {
|
mutationFn: (data: {
|
||||||
model: string
|
model: string
|
||||||
model_type: ModelTypeEnum
|
model_type: ModelTypeEnum
|
||||||
}) => del<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials`, {
|
}) => del<{ result: string }>(`/workspaces/current/model-providers/${provider}/models`, {
|
||||||
body: data,
|
body: data,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue