mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 17:18:40 +08:00
model auth
This commit is contained in:
parent
e69797d738
commit
61be6f5d2c
@ -1,34 +1,52 @@
|
|||||||
import { useCallback } from 'react'
|
import {
|
||||||
|
isValidElement,
|
||||||
|
useCallback,
|
||||||
|
} from 'react'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { FormSchema } from '../types'
|
import type { FormSchema } from '../types'
|
||||||
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
|
|
||||||
export const useGetValidators = () => {
|
export const useGetValidators = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const renderI18nObject = useRenderI18nObject()
|
||||||
|
const getLabel = useCallback((label: string | Record<string, string> | ReactNode) => {
|
||||||
|
if (isValidElement(label))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if (typeof label === 'string')
|
||||||
|
return label
|
||||||
|
|
||||||
|
if (typeof label === 'object' && label !== null)
|
||||||
|
return renderI18nObject(label as Record<string, string>)
|
||||||
|
}, [])
|
||||||
const getValidators = useCallback((formSchema: FormSchema) => {
|
const getValidators = useCallback((formSchema: FormSchema) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
validators,
|
validators,
|
||||||
required,
|
required,
|
||||||
|
label,
|
||||||
} = formSchema
|
} = formSchema
|
||||||
let mergedValidators = validators
|
let mergedValidators = validators
|
||||||
|
const memorizedLabel = getLabel(label)
|
||||||
if (required && !validators) {
|
if (required && !validators) {
|
||||||
mergedValidators = {
|
mergedValidators = {
|
||||||
onMount: ({ value }: any) => {
|
onMount: ({ value }: any) => {
|
||||||
if (!value)
|
if (!value)
|
||||||
return t('common.errorMsg.fieldRequired', { field: name })
|
return t('common.errorMsg.fieldRequired', { field: memorizedLabel || name })
|
||||||
},
|
},
|
||||||
onChange: ({ value }: any) => {
|
onChange: ({ value }: any) => {
|
||||||
if (!value)
|
if (!value)
|
||||||
return t('common.errorMsg.fieldRequired', { field: name })
|
return t('common.errorMsg.fieldRequired', { field: memorizedLabel || name })
|
||||||
},
|
},
|
||||||
onBlur: ({ value }: any) => {
|
onBlur: ({ value }: any) => {
|
||||||
if (!value)
|
if (!value)
|
||||||
return t('common.errorMsg.fieldRequired', { field: name })
|
return t('common.errorMsg.fieldRequired', { field: memorizedLabel })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mergedValidators
|
return mergedValidators
|
||||||
}, [t])
|
}, [t, getLabel])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getValidators,
|
getValidators,
|
||||||
|
|||||||
@ -181,6 +181,11 @@ export type QuotaConfiguration = {
|
|||||||
is_valid: boolean
|
is_valid: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Credential = {
|
||||||
|
credential_id: string
|
||||||
|
credential_name: string
|
||||||
|
}
|
||||||
|
|
||||||
export type ModelProvider = {
|
export type ModelProvider = {
|
||||||
provider: string
|
provider: string
|
||||||
label: TypeWithI18N
|
label: TypeWithI18N
|
||||||
@ -207,6 +212,9 @@ export type ModelProvider = {
|
|||||||
preferred_provider_type: PreferredProviderTypeEnum
|
preferred_provider_type: PreferredProviderTypeEnum
|
||||||
custom_configuration: {
|
custom_configuration: {
|
||||||
status: CustomConfigurationStatusEnum
|
status: CustomConfigurationStatusEnum
|
||||||
|
current_credential_id?: string
|
||||||
|
current_credential_name?: string
|
||||||
|
available_credentials?: Credential[]
|
||||||
}
|
}
|
||||||
system_configuration: {
|
system_configuration: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
import useSWR, { useSWRConfig } from 'swr'
|
import useSWR, { useSWRConfig } from 'swr'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import type {
|
import type {
|
||||||
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
DefaultModel,
|
DefaultModel,
|
||||||
DefaultModelResponse,
|
DefaultModelResponse,
|
||||||
@ -77,16 +78,17 @@ export const useProviderCredentialsAndLoadBalancing = (
|
|||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
configured?: boolean,
|
configured?: boolean,
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
credentialId?: string,
|
||||||
) => {
|
) => {
|
||||||
const { data: predefinedFormSchemasValue, mutate: mutatePredefined } = useSWR(
|
const { data: predefinedFormSchemasValue, mutate: mutatePredefined } = useSWR(
|
||||||
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured)
|
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
|
||||||
? `/workspaces/current/model-providers/${provider}/credentials`
|
? `/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`
|
||||||
: null,
|
: null,
|
||||||
fetchModelProviderCredentials,
|
fetchModelProviderCredentials,
|
||||||
)
|
)
|
||||||
const { data: customFormSchemasValue, mutate: mutateCustomized } = useSWR(
|
const { data: customFormSchemasValue, mutate: mutateCustomized } = useSWR(
|
||||||
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields)
|
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields && credentialId)
|
||||||
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}`
|
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`
|
||||||
: null,
|
: null,
|
||||||
fetchModelProviderCredentials,
|
fetchModelProviderCredentials,
|
||||||
)
|
)
|
||||||
@ -102,6 +104,7 @@ export const useProviderCredentialsAndLoadBalancing = (
|
|||||||
: undefined
|
: undefined
|
||||||
}, [
|
}, [
|
||||||
configurationMethod,
|
configurationMethod,
|
||||||
|
credentialId,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
customFormSchemasValue?.credentials,
|
customFormSchemasValue?.credentials,
|
||||||
predefinedFormSchemasValue?.credentials,
|
predefinedFormSchemasValue?.credentials,
|
||||||
@ -313,40 +316,53 @@ export const useMarketplaceAllPlugins = (providers: ModelProvider[], searchText:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useModelModalHandler = () => {
|
export const useRefreshModel = () => {
|
||||||
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
const updateModelProviders = useUpdateModelProviders()
|
const updateModelProviders = useUpdateModelProviders()
|
||||||
const updateModelList = useUpdateModelList()
|
const updateModelList = useUpdateModelList()
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const handleRefreshModel = useCallback((provider: ModelProvider, configurationMethod: ConfigurationMethodEnum, CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => {
|
||||||
|
updateModelProviders()
|
||||||
|
|
||||||
|
provider.supported_model_types.forEach((type) => {
|
||||||
|
updateModelList(type)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||||
|
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||||
|
eventEmitter?.emit({
|
||||||
|
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||||
|
payload: provider.provider,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
if (CustomConfigurationModelFixedFields?.__model_type)
|
||||||
|
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
||||||
|
}
|
||||||
|
}, [eventEmitter, updateModelList, updateModelProviders])
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleRefreshModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useModelModalHandler = () => {
|
||||||
|
const setShowModelModal = useModalContextSelector(state => state.setShowModelModal)
|
||||||
|
const { handleRefreshModel } = useRefreshModel()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
provider: ModelProvider,
|
provider: ModelProvider,
|
||||||
configurationMethod: ConfigurationMethodEnum,
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
credential?: Credential,
|
||||||
) => {
|
) => {
|
||||||
setShowModelModal({
|
setShowModelModal({
|
||||||
payload: {
|
payload: {
|
||||||
currentProvider: provider,
|
currentProvider: provider,
|
||||||
currentConfigurationMethod: configurationMethod,
|
currentConfigurationMethod: configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||||
|
credential,
|
||||||
},
|
},
|
||||||
onSaveCallback: () => {
|
onSaveCallback: () => {
|
||||||
updateModelProviders()
|
handleRefreshModel(provider, configurationMethod, CustomConfigurationModelFixedFields)
|
||||||
|
|
||||||
provider.supported_model_types.forEach((type) => {
|
|
||||||
updateModelList(type)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (configurationMethod === ConfigurationMethodEnum.customizableModel
|
|
||||||
&& provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
|
||||||
eventEmitter?.emit({
|
|
||||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
|
||||||
payload: provider.provider,
|
|
||||||
} as any)
|
|
||||||
|
|
||||||
if (CustomConfigurationModelFixedFields?.__model_type)
|
|
||||||
updateModelList(CustomConfigurationModelFixedFields.__model_type)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import SystemModelSelector from './system-model-selector'
|
|||||||
import ProviderAddedCard from './provider-added-card'
|
import ProviderAddedCard from './provider-added-card'
|
||||||
import type {
|
import type {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
@ -126,7 +127,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
|||||||
<ProviderAddedCard
|
<ProviderAddedCard
|
||||||
key={provider.provider}
|
key={provider.provider}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields)}
|
onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, credential?: Credential) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields, credential)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -140,7 +141,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
|||||||
notConfigured
|
notConfigured
|
||||||
key={provider.provider}
|
key={provider.provider}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields)}
|
onOpenModal={(configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, credential?: Credential) => handleOpenModal(provider, configurationMethod, currentCustomConfigurationModelFixedFields, credential)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,83 +0,0 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import { useCallback } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import {
|
|
||||||
RiEqualizer2Line,
|
|
||||||
RiMoreFill,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import type { ModelProvider } from '../declarations'
|
|
||||||
import {
|
|
||||||
CustomConfigurationStatusEnum,
|
|
||||||
} from '../declarations'
|
|
||||||
import Indicator from '@/app/components/header/indicator'
|
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import {
|
|
||||||
AuthCategory,
|
|
||||||
Authorized,
|
|
||||||
} from '@/app/components/plugins/plugin-auth'
|
|
||||||
|
|
||||||
type AuthPanelProps = {
|
|
||||||
provider: ModelProvider
|
|
||||||
onSetup: () => void
|
|
||||||
}
|
|
||||||
const AuthPanel: FC<AuthPanelProps> = ({
|
|
||||||
provider,
|
|
||||||
onSetup,
|
|
||||||
}) => {
|
|
||||||
const authorized = false
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const customConfig = provider.custom_configuration
|
|
||||||
const isCustomConfigured = customConfig.status === CustomConfigurationStatusEnum.active
|
|
||||||
|
|
||||||
const renderTrigger = useCallback(() => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
className='h-6 w-6'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
<RiMoreFill className='h-4 w-4' />
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{
|
|
||||||
provider.provider_credential_schema && (
|
|
||||||
<div className='relative ml-1 w-[112px] shrink-0 rounded-lg border-[0.5px] border-components-panel-border bg-white/[0.18] p-1'>
|
|
||||||
<div className='system-xs-medium-uppercase mb-1 flex h-5 items-center justify-between pl-2 pr-[7px] pt-1 text-text-tertiary'>
|
|
||||||
API-KEY
|
|
||||||
<Indicator color={isCustomConfigured ? 'green' : 'red'} />
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center gap-0.5'>
|
|
||||||
<Button
|
|
||||||
className='mr-0.5 grow'
|
|
||||||
size='small'
|
|
||||||
onClick={onSetup}
|
|
||||||
>
|
|
||||||
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{
|
|
||||||
authorized ? t('common.operation.config') : t('common.operation.setup')
|
|
||||||
}
|
|
||||||
</Button>
|
|
||||||
{
|
|
||||||
authorized && (
|
|
||||||
<Authorized
|
|
||||||
pluginPayload={{
|
|
||||||
category: AuthCategory.model,
|
|
||||||
provider: provider.provider,
|
|
||||||
}}
|
|
||||||
credentials={[]}
|
|
||||||
renderTrigger={renderTrigger}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AuthPanel
|
|
||||||
@ -0,0 +1,203 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import {
|
||||||
|
RiEqualizer2Line,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import type {
|
||||||
|
PortalToFollowElemOptions,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import Confirm from '@/app/components/base/confirm'
|
||||||
|
import Item from './item'
|
||||||
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
import type { Credential } from '../../declarations'
|
||||||
|
import { useDeleteModelCredential } from '@/service/use-models'
|
||||||
|
|
||||||
|
type AuthorizedProps = {
|
||||||
|
provider: string
|
||||||
|
credentials: Credential[]
|
||||||
|
disabled?: boolean
|
||||||
|
renderTrigger?: (open?: boolean) => React.ReactNode
|
||||||
|
isOpen?: boolean
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
|
offset?: PortalToFollowElemOptions['offset']
|
||||||
|
placement?: PortalToFollowElemOptions['placement']
|
||||||
|
triggerPopupSameWidth?: boolean
|
||||||
|
popupClassName?: string
|
||||||
|
onItemClick?: (id: string) => void
|
||||||
|
showItemSelectedIcon?: boolean
|
||||||
|
selectedCredentialId?: string
|
||||||
|
onUpdate?: () => void
|
||||||
|
onSetup: (credential?: Credential) => void
|
||||||
|
}
|
||||||
|
const Authorized = ({
|
||||||
|
provider,
|
||||||
|
credentials,
|
||||||
|
disabled,
|
||||||
|
renderTrigger,
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
offset = 8,
|
||||||
|
placement = 'bottom-end',
|
||||||
|
triggerPopupSameWidth = false,
|
||||||
|
popupClassName,
|
||||||
|
onItemClick,
|
||||||
|
showItemSelectedIcon,
|
||||||
|
selectedCredentialId,
|
||||||
|
onUpdate,
|
||||||
|
onSetup,
|
||||||
|
}: AuthorizedProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { notify } = useToastContext()
|
||||||
|
const [isLocalOpen, setIsLocalOpen] = useState(false)
|
||||||
|
const mergedIsOpen = isOpen ?? isLocalOpen
|
||||||
|
const setMergedIsOpen = useCallback((open: boolean) => {
|
||||||
|
if (onOpenChange)
|
||||||
|
onOpenChange(open)
|
||||||
|
|
||||||
|
setIsLocalOpen(open)
|
||||||
|
}, [onOpenChange])
|
||||||
|
const pendingOperationCredentialId = useRef<string | null>(null)
|
||||||
|
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||||
|
const openConfirm = useCallback((credentialId?: string) => {
|
||||||
|
if (credentialId)
|
||||||
|
pendingOperationCredentialId.current = credentialId
|
||||||
|
|
||||||
|
setDeleteCredentialId(pendingOperationCredentialId.current)
|
||||||
|
}, [])
|
||||||
|
const closeConfirm = useCallback(() => {
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
}, [])
|
||||||
|
const [doingAction, setDoingAction] = useState(false)
|
||||||
|
const doingActionRef = useRef(doingAction)
|
||||||
|
const handleSetDoingAction = useCallback((doing: boolean) => {
|
||||||
|
doingActionRef.current = doing
|
||||||
|
setDoingAction(doing)
|
||||||
|
}, [])
|
||||||
|
const { mutateAsync: deleteModelCredential } = useDeleteModelCredential(provider)
|
||||||
|
const handleConfirm = useCallback(async () => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
if (!pendingOperationCredentialId.current) {
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await deleteModelCredential(pendingOperationCredentialId.current)
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [onUpdate, notify, t, handleSetDoingAction])
|
||||||
|
const handleEdit = useCallback((credential: Credential) => {
|
||||||
|
onSetup(credential)
|
||||||
|
}, [onSetup])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={mergedIsOpen}
|
||||||
|
onOpenChange={setMergedIsOpen}
|
||||||
|
placement={placement}
|
||||||
|
offset={offset}
|
||||||
|
triggerPopupSameWidth={triggerPopupSameWidth}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger
|
||||||
|
onClick={() => setMergedIsOpen(!mergedIsOpen)}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
{
|
||||||
|
renderTrigger
|
||||||
|
? renderTrigger(mergedIsOpen)
|
||||||
|
: (
|
||||||
|
<Button
|
||||||
|
className='grow'
|
||||||
|
size='small'
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{t('common.operation.config')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
|
<div className={cn(
|
||||||
|
'max-h-[360px] w-[360px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
|
||||||
|
popupClassName,
|
||||||
|
)}>
|
||||||
|
<div className='py-1'>
|
||||||
|
{
|
||||||
|
!!credentials.length && (
|
||||||
|
<div className='p-1'>
|
||||||
|
<div className={cn(
|
||||||
|
'system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary',
|
||||||
|
showItemSelectedIcon && 'pl-7',
|
||||||
|
)}>
|
||||||
|
API Keys
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
credentials.map(credential => (
|
||||||
|
<Item
|
||||||
|
key={credential.credential_id}
|
||||||
|
credential={credential}
|
||||||
|
disabled={disabled}
|
||||||
|
onDelete={openConfirm}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
onItemClick={onItemClick}
|
||||||
|
showSelectedIcon={showItemSelectedIcon}
|
||||||
|
selectedCredentialId={selectedCredentialId}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
|
<div className='p-2'>
|
||||||
|
<Button
|
||||||
|
onClick={() => onSetup()}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
add api key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
{
|
||||||
|
deleteCredentialId && (
|
||||||
|
<Confirm
|
||||||
|
isShow
|
||||||
|
title={t('datasetDocuments.list.delete.title')}
|
||||||
|
isDisabled={doingAction}
|
||||||
|
onCancel={closeConfirm}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Authorized)
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useMemo,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiCheckLine,
|
||||||
|
RiDeleteBinLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import type { Credential } from '../../declarations'
|
||||||
|
|
||||||
|
type ItemProps = {
|
||||||
|
credential: Credential
|
||||||
|
disabled?: boolean
|
||||||
|
onDelete?: (id: string) => void
|
||||||
|
onEdit?: (credential: Credential) => void
|
||||||
|
onSetDefault?: (id: string) => void
|
||||||
|
disableRename?: boolean
|
||||||
|
disableEdit?: boolean
|
||||||
|
disableDelete?: boolean
|
||||||
|
disableSetDefault?: boolean
|
||||||
|
onItemClick?: (id: string) => void
|
||||||
|
showSelectedIcon?: boolean
|
||||||
|
selectedCredentialId?: string
|
||||||
|
}
|
||||||
|
const Item = ({
|
||||||
|
credential,
|
||||||
|
disabled,
|
||||||
|
onDelete,
|
||||||
|
onEdit,
|
||||||
|
disableRename,
|
||||||
|
disableEdit,
|
||||||
|
disableDelete,
|
||||||
|
disableSetDefault,
|
||||||
|
onItemClick,
|
||||||
|
showSelectedIcon,
|
||||||
|
selectedCredentialId,
|
||||||
|
}: ItemProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const showAction = useMemo(() => {
|
||||||
|
return !(disableRename && disableEdit && disableDelete && disableSetDefault)
|
||||||
|
}, [disableRename, disableEdit, disableDelete, disableSetDefault])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={credential.credential_id}
|
||||||
|
className={cn(
|
||||||
|
'group flex h-8 items-center rounded-lg p-1 hover:bg-state-base-hover',
|
||||||
|
)}
|
||||||
|
onClick={() => onItemClick?.(credential.credential_id)}
|
||||||
|
>
|
||||||
|
<div className='flex w-0 grow items-center space-x-1.5'>
|
||||||
|
{
|
||||||
|
showSelectedIcon && (
|
||||||
|
<div className='h-4 w-4'>
|
||||||
|
{
|
||||||
|
selectedCredentialId === credential.credential_id && (
|
||||||
|
<RiCheckLine className='h-4 w-4 text-text-accent' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Indicator className='ml-2 mr-1.5 shrink-0' />
|
||||||
|
<div
|
||||||
|
className='system-md-regular truncate text-text-secondary'
|
||||||
|
title={credential.credential_name}
|
||||||
|
>
|
||||||
|
{credential.credential_name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
showAction && (
|
||||||
|
<div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
|
||||||
|
{
|
||||||
|
!disableEdit && (
|
||||||
|
<Tooltip popupContent={t('common.operation.edit')}>
|
||||||
|
<ActionButton
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onEdit?.(credential)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!disableDelete && (
|
||||||
|
<Tooltip popupContent={t('common.operation.delete')}>
|
||||||
|
<ActionButton
|
||||||
|
className='hover:bg-transparent'
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onDelete?.(credential.credential_id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary hover:text-text-destructive' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Item)
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type {
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
|
import {
|
||||||
|
genModelNameFormSchema,
|
||||||
|
genModelTypeFormSchema,
|
||||||
|
} from '../utils'
|
||||||
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
|
|
||||||
|
export const useModelFormSchemas = (
|
||||||
|
provider: ModelProvider,
|
||||||
|
providerFormSchemaPredefined: boolean,
|
||||||
|
draftConfig?: ModelLoadBalancingConfig,
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
provider_credential_schema,
|
||||||
|
supported_model_types,
|
||||||
|
model_credential_schema,
|
||||||
|
} = provider
|
||||||
|
const formSchemas = useMemo(() => {
|
||||||
|
return providerFormSchemaPredefined
|
||||||
|
? provider_credential_schema.credential_form_schemas
|
||||||
|
: [
|
||||||
|
genModelTypeFormSchema(supported_model_types),
|
||||||
|
genModelNameFormSchema(model_credential_schema?.model),
|
||||||
|
...(draftConfig?.enabled ? [] : model_credential_schema.credential_form_schemas),
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
providerFormSchemaPredefined,
|
||||||
|
provider_credential_schema?.credential_form_schemas,
|
||||||
|
supported_model_types,
|
||||||
|
model_credential_schema?.credential_form_schemas,
|
||||||
|
model_credential_schema?.model,
|
||||||
|
draftConfig?.enabled,
|
||||||
|
])
|
||||||
|
|
||||||
|
const formSchemasWithAuthorizationName = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
variable: '__authorization_name__',
|
||||||
|
label: t('plugin.auth.authorizationName'),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
...formSchemas,
|
||||||
|
]
|
||||||
|
}, [formSchemas, t])
|
||||||
|
|
||||||
|
return {
|
||||||
|
formSchemas: formSchemasWithAuthorizationName,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,11 +45,14 @@ import type {
|
|||||||
FormRefObject,
|
FormRefObject,
|
||||||
FormSchema,
|
FormSchema,
|
||||||
} from '@/app/components/base/form/types'
|
} from '@/app/components/base/form/types'
|
||||||
|
import { useModelFormSchemas } from '../model-auth/hooks'
|
||||||
|
import type { Credential } from '../declarations'
|
||||||
|
|
||||||
type ModelModalProps = {
|
type ModelModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
configurateMethod: ConfigurationMethodEnum
|
configurateMethod: ConfigurationMethodEnum
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
|
credential?: Credential
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onSave: () => void
|
onSave: () => void
|
||||||
}
|
}
|
||||||
@ -58,6 +61,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
provider,
|
provider,
|
||||||
configurateMethod,
|
configurateMethod,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
credential,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSave,
|
onSave,
|
||||||
}) => {
|
}) => {
|
||||||
@ -71,6 +75,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
configurateMethod,
|
configurateMethod,
|
||||||
providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active,
|
providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
credential?.credential_id,
|
||||||
)
|
)
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
||||||
@ -95,22 +100,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
setDraftConfig(originalConfig)
|
setDraftConfig(originalConfig)
|
||||||
}, [draftConfig, originalConfig])
|
}, [draftConfig, originalConfig])
|
||||||
|
|
||||||
const formSchemas = useMemo(() => {
|
const { formSchemas } = useModelFormSchemas(provider, providerFormSchemaPredefined, draftConfig)
|
||||||
return providerFormSchemaPredefined
|
|
||||||
? provider.provider_credential_schema.credential_form_schemas
|
|
||||||
: [
|
|
||||||
genModelTypeFormSchema(provider.supported_model_types),
|
|
||||||
genModelNameFormSchema(provider.model_credential_schema?.model),
|
|
||||||
...(draftConfig?.enabled ? [] : provider.model_credential_schema.credential_form_schemas),
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
providerFormSchemaPredefined,
|
|
||||||
provider.provider_credential_schema?.credential_form_schemas,
|
|
||||||
provider.supported_model_types,
|
|
||||||
provider.model_credential_schema?.credential_form_schemas,
|
|
||||||
provider.model_credential_schema?.model,
|
|
||||||
draftConfig?.enabled,
|
|
||||||
])
|
|
||||||
const formRef = useRef<FormRefObject>(null)
|
const formRef = useRef<FormRefObject>(null)
|
||||||
|
|
||||||
const extendedSecretFormSchemas = useMemo(
|
const extendedSecretFormSchemas = useMemo(
|
||||||
@ -152,6 +142,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
}) || { isCheckValidated: false, values: {} }
|
}) || { isCheckValidated: false, values: {} }
|
||||||
if (!isCheckValidated)
|
if (!isCheckValidated)
|
||||||
return
|
return
|
||||||
|
|
||||||
const res = await saveCredentials(
|
const res = await saveCredentials(
|
||||||
providerFormSchemaPredefined,
|
providerFormSchemaPredefined,
|
||||||
provider.provider,
|
provider.provider,
|
||||||
@ -190,6 +181,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
providerFormSchemaPredefined,
|
providerFormSchemaPredefined,
|
||||||
provider.provider,
|
provider.provider,
|
||||||
values,
|
values,
|
||||||
|
credential?.credential_id,
|
||||||
)
|
)
|
||||||
if (res.result === 'success') {
|
if (res.result === 'success') {
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
@ -227,7 +219,10 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
||||||
}
|
}
|
||||||
}) as FormSchema[]}
|
}) as FormSchema[]}
|
||||||
defaultValues={formSchemasValue}
|
defaultValues={{
|
||||||
|
...formSchemasValue,
|
||||||
|
__authorization_name__: credential?.credential_name,
|
||||||
|
}}
|
||||||
inputClassName='justify-start'
|
inputClassName='justify-start'
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import type { FC } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { RiEqualizer2Line } from '@remixicon/react'
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
import type { ModelProvider } from '../declarations'
|
import type {
|
||||||
|
Credential,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
CustomConfigurationStatusEnum,
|
CustomConfigurationStatusEnum,
|
||||||
@ -19,15 +22,18 @@ import Button from '@/app/components/base/button'
|
|||||||
import { changeModelProviderPriority } from '@/service/common'
|
import { changeModelProviderPriority } from '@/service/common'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
|
import Authorized from '../model-auth/authorized'
|
||||||
|
|
||||||
type CredentialPanelProps = {
|
type CredentialPanelProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
onSetup: () => void
|
onSetup: (credential?: Credential) => void
|
||||||
|
onUpdate: () => void
|
||||||
}
|
}
|
||||||
const CredentialPanel: FC<CredentialPanelProps> = ({
|
const CredentialPanel = ({
|
||||||
provider,
|
provider,
|
||||||
onSetup,
|
onSetup,
|
||||||
}) => {
|
onUpdate,
|
||||||
|
}: CredentialPanelProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
@ -38,6 +44,13 @@ const CredentialPanel: FC<CredentialPanelProps> = ({
|
|||||||
const priorityUseType = provider.preferred_provider_type
|
const priorityUseType = provider.preferred_provider_type
|
||||||
const isCustomConfigured = customConfig.status === CustomConfigurationStatusEnum.active
|
const isCustomConfigured = customConfig.status === CustomConfigurationStatusEnum.active
|
||||||
const configurateMethods = provider.configurate_methods
|
const configurateMethods = provider.configurate_methods
|
||||||
|
const {
|
||||||
|
current_credential_id,
|
||||||
|
current_credential_name,
|
||||||
|
available_credentials,
|
||||||
|
} = provider.custom_configuration
|
||||||
|
const authorized = current_credential_id && current_credential_name && available_credentials?.every(item => !!item.credential_id)
|
||||||
|
const authRemoved = !!available_credentials?.length && available_credentials?.every(item => !item.credential_id)
|
||||||
|
|
||||||
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
||||||
const res = await changeModelProviderPriority({
|
const res = await changeModelProviderPriority({
|
||||||
@ -61,25 +74,58 @@ const CredentialPanel: FC<CredentialPanelProps> = ({
|
|||||||
} as any)
|
} as any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const credentialLabel = useMemo(() => {
|
||||||
|
if (authorized)
|
||||||
|
return current_credential_name
|
||||||
|
if (authRemoved)
|
||||||
|
return 'Auth removed'
|
||||||
|
return 'Unauthorized'
|
||||||
|
}, [authorized, authRemoved, current_credential_name])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
provider.provider_credential_schema && (
|
provider.provider_credential_schema && (
|
||||||
<div className='relative ml-1 w-[112px] shrink-0 rounded-lg border-[0.5px] border-components-panel-border bg-white/[0.18] p-1'>
|
<div className='relative ml-1 w-[112px] shrink-0 rounded-lg border-[0.5px] border-components-panel-border bg-white/[0.18] p-1'>
|
||||||
<div className='system-xs-medium-uppercase mb-1 flex h-5 items-center justify-between pl-2 pr-[7px] pt-1 text-text-tertiary'>
|
<div className='system-xs-medium mb-1 flex h-5 items-center justify-between pl-2 pr-[7px] pt-1 text-text-tertiary'>
|
||||||
API-KEY
|
<div
|
||||||
<Indicator color={isCustomConfigured ? 'green' : 'red'} />
|
className='grow truncate'
|
||||||
|
title={credentialLabel}
|
||||||
|
>
|
||||||
|
{credentialLabel}
|
||||||
|
</div>
|
||||||
|
<Indicator className='shrink-0' color={authorized ? 'green' : 'red'} />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-0.5'>
|
<div className='flex items-center gap-0.5'>
|
||||||
<Button
|
{
|
||||||
className='grow'
|
(!authorized || authRemoved) && (
|
||||||
size='small'
|
<Button
|
||||||
onClick={onSetup}
|
className='grow'
|
||||||
>
|
size='small'
|
||||||
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
onClick={() => onSetup()}
|
||||||
{t('common.operation.setup')}
|
variant={!authorized ? 'secondary-accent' : 'secondary'}
|
||||||
</Button>
|
>
|
||||||
|
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{
|
||||||
|
authRemoved
|
||||||
|
? t('common.operation.config')
|
||||||
|
: t('common.operation.setup')
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
authorized && (
|
||||||
|
<Authorized
|
||||||
|
provider={provider.provider}
|
||||||
|
onSetup={onSetup}
|
||||||
|
credentials={available_credentials ?? []}
|
||||||
|
selectedCredentialId={current_credential_id}
|
||||||
|
showItemSelectedIcon
|
||||||
|
onUpdate={onUpdate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
systemConfig.enabled && isCustomConfigured && (
|
systemConfig.enabled && isCustomConfigured && (
|
||||||
<PrioritySelector
|
<PrioritySelector
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import type {
|
|||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import { ConfigurationMethodEnum } from '../declarations'
|
import { ConfigurationMethodEnum } from '../declarations'
|
||||||
|
import type { Credential } from '../declarations'
|
||||||
import {
|
import {
|
||||||
MODEL_PROVIDER_QUOTA_GET_PAID,
|
MODEL_PROVIDER_QUOTA_GET_PAID,
|
||||||
modelTypeFormat,
|
modelTypeFormat,
|
||||||
@ -27,12 +28,13 @@ 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 { useRefreshModel } from '../hooks'
|
||||||
|
|
||||||
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 = {
|
||||||
notConfigured?: boolean
|
notConfigured?: boolean
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
onOpenModal: (configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => void
|
onOpenModal: (configurationMethod: ConfigurationMethodEnum, currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, credential?: Credential) => void
|
||||||
}
|
}
|
||||||
const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
||||||
notConfigured,
|
notConfigured,
|
||||||
@ -80,6 +82,8 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
|||||||
getModelList(v.payload)
|
getModelList(v.payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { handleRefreshModel } = useRefreshModel()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -114,7 +118,8 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
|||||||
{
|
{
|
||||||
showCredential && (
|
showCredential && (
|
||||||
<CredentialPanel
|
<CredentialPanel
|
||||||
onSetup={() => onOpenModal(ConfigurationMethodEnum.predefinedModel)}
|
onSetup={(credential?: Credential) => onOpenModal(ConfigurationMethodEnum.predefinedModel, undefined, credential)}
|
||||||
|
onUpdate={() => handleRefreshModel(provider, ConfigurationMethodEnum.predefinedModel, undefined)}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -82,12 +82,14 @@ export const saveCredentials = async (predefined: boolean, provider: string, v:
|
|||||||
let body, url
|
let body, url
|
||||||
|
|
||||||
if (predefined) {
|
if (predefined) {
|
||||||
|
const { __authorization_name__, ...rest } = v
|
||||||
body = {
|
body = {
|
||||||
config_from: ConfigurationMethodEnum.predefinedModel,
|
config_from: ConfigurationMethodEnum.predefinedModel,
|
||||||
credentials: v,
|
credentials: rest,
|
||||||
load_balancing: loadBalancing,
|
load_balancing: loadBalancing,
|
||||||
|
name: __authorization_name__,
|
||||||
}
|
}
|
||||||
url = `/workspaces/current/model-providers/${provider}`
|
url = `/workspaces/current/model-providers/${provider}/credentials`
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const { __model_name, __model_type, ...credentials } = v
|
const { __model_name, __model_type, ...credentials } = v
|
||||||
@ -117,12 +119,17 @@ export const savePredefinedLoadBalancingConfig = async (provider: string, v: For
|
|||||||
return setModelProvider({ url, body })
|
return setModelProvider({ url, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const removeCredentials = async (predefined: boolean, provider: string, v: FormValue) => {
|
export const removeCredentials = async (predefined: boolean, provider: string, v: FormValue, credentialId?: string) => {
|
||||||
let url = ''
|
let url = ''
|
||||||
let body
|
let body
|
||||||
|
|
||||||
if (predefined) {
|
if (predefined) {
|
||||||
url = `/workspaces/current/model-providers/${provider}`
|
url = `/workspaces/current/model-providers/${provider}/credentials`
|
||||||
|
if (credentialId) {
|
||||||
|
body = {
|
||||||
|
credential_id: credentialId,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (v) {
|
if (v) {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { createContext, useContext, useContextSelector } from 'use-context-selec
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import type {
|
import type {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
|
Credential,
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
ModelLoadBalancingConfigEntry,
|
ModelLoadBalancingConfigEntry,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
@ -79,6 +80,7 @@ export type ModelModalType = {
|
|||||||
currentProvider: ModelProvider
|
currentProvider: ModelProvider
|
||||||
currentConfigurationMethod: ConfigurationMethodEnum
|
currentConfigurationMethod: ConfigurationMethodEnum
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
|
credential?: Credential
|
||||||
}
|
}
|
||||||
export type LoadBalancingEntryModalType = ModelModalType & {
|
export type LoadBalancingEntryModalType = ModelModalType & {
|
||||||
entry?: ModelLoadBalancingConfigEntry
|
entry?: ModelLoadBalancingConfigEntry
|
||||||
@ -337,6 +339,7 @@ export const ModalContextProvider = ({
|
|||||||
provider={showModelModal.payload.currentProvider}
|
provider={showModelModal.payload.currentProvider}
|
||||||
configurateMethod={showModelModal.payload.currentConfigurationMethod}
|
configurateMethod={showModelModal.payload.currentConfigurationMethod}
|
||||||
currentCustomConfigurationModelFixedFields={showModelModal.payload.currentCustomConfigurationModelFixedFields}
|
currentCustomConfigurationModelFixedFields={showModelModal.payload.currentCustomConfigurationModelFixedFields}
|
||||||
|
credential={showModelModal.payload.credential}
|
||||||
onCancel={handleCancelModelModal}
|
onCancel={handleCancelModelModal}
|
||||||
onSave={handleSaveModelModal}
|
onSave={handleSaveModelModal}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
import { get } from './base'
|
import {
|
||||||
|
del,
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
} from './base'
|
||||||
import type {
|
import type {
|
||||||
ModelItem,
|
ModelItem,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import {
|
import {
|
||||||
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
// useQueryClient,
|
// useQueryClient,
|
||||||
} from '@tanstack/react-query'
|
} from '@tanstack/react-query'
|
||||||
@ -15,3 +20,26 @@ export const useModelProviderModelList = (provider: string) => {
|
|||||||
queryFn: () => get<{ data: ModelItem[] }>(`/workspaces/current/model-providers/${provider}/models`),
|
queryFn: () => get<{ data: ModelItem[] }>(`/workspaces/current/model-providers/${provider}/models`),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useAddModelCredential = (providerName: string) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (data: any) => post<{ result: string }>(`/workspaces/current/model-providers/${providerName}/credentials`, data),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetModelCredential = (providerName: string, credentialId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'model-credential', providerName, credentialId],
|
||||||
|
queryFn: () => get<{ data: Credential[] }>(`/workspaces/current/model-providers/${providerName}/credentials?credential_id=${credentialId}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDeleteModelCredential = (providerName: string) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (credentialId: string) => del<{ result: string }>(`/workspaces/current/model-providers/${providerName}/credentials`, {
|
||||||
|
body: {
|
||||||
|
credential_id: credentialId,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user