mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 12:37:20 +08:00
model auth
This commit is contained in:
parent
4e02abf784
commit
3f57e4a643
@ -80,13 +80,13 @@ export const useProviderCredentialsAndLoadBalancing = (
|
|||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
credentialId?: string,
|
credentialId?: string,
|
||||||
) => {
|
) => {
|
||||||
const { data: predefinedFormSchemasValue, mutate: mutatePredefined } = useSWR(
|
const { data: predefinedFormSchemasValue, mutate: mutatePredefined, isLoading: isPredefinedLoading } = useSWR(
|
||||||
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
|
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
|
||||||
? `/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`
|
? `/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, isLoading: isCustomizedLoading } = useSWR(
|
||||||
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields && credentialId)
|
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields && credentialId)
|
||||||
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`
|
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`
|
||||||
: null,
|
: null,
|
||||||
@ -122,6 +122,7 @@ export const useProviderCredentialsAndLoadBalancing = (
|
|||||||
: customFormSchemasValue
|
: customFormSchemasValue
|
||||||
)?.load_balancing,
|
)?.load_balancing,
|
||||||
mutate,
|
mutate,
|
||||||
|
isLoading: isPredefinedLoading || isCustomizedLoading,
|
||||||
}
|
}
|
||||||
// as ([Record<string, string | boolean | undefined> | undefined, ModelLoadBalancingConfig | undefined])
|
// as ([Record<string, string | boolean | undefined> | undefined, ModelLoadBalancingConfig | undefined])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,10 @@ import Confirm from '@/app/components/base/confirm'
|
|||||||
import Item from './item'
|
import Item from './item'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import type { Credential } from '../../declarations'
|
import type { Credential } from '../../declarations'
|
||||||
import { useDeleteModelCredential } from '@/service/use-models'
|
import {
|
||||||
|
useDeleteModelCredential,
|
||||||
|
useSetModelCredentialDefault,
|
||||||
|
} from '@/service/use-models'
|
||||||
|
|
||||||
type AuthorizedProps = {
|
type AuthorizedProps = {
|
||||||
provider: string
|
provider: string
|
||||||
@ -87,6 +90,23 @@ const Authorized = ({
|
|||||||
setDoingAction(doing)
|
setDoingAction(doing)
|
||||||
}, [])
|
}, [])
|
||||||
const { mutateAsync: deleteModelCredential } = useDeleteModelCredential(provider)
|
const { mutateAsync: deleteModelCredential } = useDeleteModelCredential(provider)
|
||||||
|
const { mutateAsync: setModelCredentialDefault } = useSetModelCredentialDefault(provider)
|
||||||
|
const handleSetDefault = useCallback(async (id: string) => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await setModelCredentialDefault(id)
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [setModelCredentialDefault, onUpdate, notify, t, handleSetDoingAction])
|
||||||
const handleConfirm = useCallback(async () => {
|
const handleConfirm = useCallback(async () => {
|
||||||
if (doingActionRef.current)
|
if (doingActionRef.current)
|
||||||
return
|
return
|
||||||
@ -109,9 +129,10 @@ const Authorized = ({
|
|||||||
handleSetDoingAction(false)
|
handleSetDoingAction(false)
|
||||||
}
|
}
|
||||||
}, [onUpdate, notify, t, handleSetDoingAction])
|
}, [onUpdate, notify, t, handleSetDoingAction])
|
||||||
const handleEdit = useCallback((credential: Credential) => {
|
const handleOpenSetup = useCallback((credential?: Credential) => {
|
||||||
onSetup(credential)
|
onSetup(credential)
|
||||||
}, [onSetup])
|
setMergedIsOpen(false)
|
||||||
|
}, [onSetup, setMergedIsOpen])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -142,10 +163,10 @@ const Authorized = ({
|
|||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='z-[100]'>
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
<div className={cn(
|
<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',
|
'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
|
||||||
popupClassName,
|
popupClassName,
|
||||||
)}>
|
)}>
|
||||||
<div className='py-1'>
|
<div className='max-h-[304px] overflow-y-auto py-1'>
|
||||||
{
|
{
|
||||||
!!credentials.length && (
|
!!credentials.length && (
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
@ -162,7 +183,8 @@ const Authorized = ({
|
|||||||
credential={credential}
|
credential={credential}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onDelete={openConfirm}
|
onDelete={openConfirm}
|
||||||
onEdit={handleEdit}
|
onEdit={handleOpenSetup}
|
||||||
|
onSetDefault={handleSetDefault}
|
||||||
onItemClick={onItemClick}
|
onItemClick={onItemClick}
|
||||||
showSelectedIcon={showItemSelectedIcon}
|
showSelectedIcon={showItemSelectedIcon}
|
||||||
selectedCredentialId={selectedCredentialId}
|
selectedCredentialId={selectedCredentialId}
|
||||||
@ -176,7 +198,7 @@ const Authorized = ({
|
|||||||
<div className='h-[1px] bg-divider-subtle'></div>
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
<div className='p-2'>
|
<div className='p-2'>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onSetup()}
|
onClick={() => handleOpenSetup()}
|
||||||
className='w-full'
|
className='w-full'
|
||||||
>
|
>
|
||||||
add api key
|
add api key
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import ActionButton from '@/app/components/base/action-button'
|
|||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import type { Credential } from '../../declarations'
|
import type { Credential } from '../../declarations'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
credential: Credential
|
credential: Credential
|
||||||
@ -33,6 +34,7 @@ const Item = ({
|
|||||||
disabled,
|
disabled,
|
||||||
onDelete,
|
onDelete,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
onSetDefault,
|
||||||
disableRename,
|
disableRename,
|
||||||
disableEdit,
|
disableEdit,
|
||||||
disableDelete,
|
disableDelete,
|
||||||
@ -77,6 +79,20 @@ const Item = ({
|
|||||||
{
|
{
|
||||||
showAction && (
|
showAction && (
|
||||||
<div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
|
<div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
|
||||||
|
{
|
||||||
|
!disableSetDefault && (
|
||||||
|
<Button
|
||||||
|
size='small'
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
onSetDefault?.(credential.credential_id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('plugin.auth.setDefault')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
!disableEdit && (
|
!disableEdit && (
|
||||||
<Tooltip popupContent={t('common.operation.edit')}>
|
<Tooltip popupContent={t('common.operation.edit')}>
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import type {
|
|||||||
} from '@/app/components/base/form/types'
|
} from '@/app/components/base/form/types'
|
||||||
import { useModelFormSchemas } from '../model-auth/hooks'
|
import { useModelFormSchemas } from '../model-auth/hooks'
|
||||||
import type { Credential } from '../declarations'
|
import type { Credential } from '../declarations'
|
||||||
|
import Loading from '@/app/components/base/loading'
|
||||||
|
|
||||||
type ModelModalProps = {
|
type ModelModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
@ -70,6 +71,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
credentials: formSchemasValue,
|
credentials: formSchemasValue,
|
||||||
loadBalancing: originalConfig,
|
loadBalancing: originalConfig,
|
||||||
mutate,
|
mutate,
|
||||||
|
isLoading,
|
||||||
} = useProviderCredentialsAndLoadBalancing(
|
} = useProviderCredentialsAndLoadBalancing(
|
||||||
provider.provider,
|
provider.provider,
|
||||||
configurateMethod,
|
configurateMethod,
|
||||||
@ -185,7 +187,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
)
|
)
|
||||||
if (res.result === 'success') {
|
if (res.result === 'success') {
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||||
mutate()
|
// mutate()
|
||||||
onSave()
|
onSave()
|
||||||
onCancel()
|
onCancel()
|
||||||
}
|
}
|
||||||
@ -211,21 +213,32 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='max-h-[calc(100vh-320px)] overflow-y-auto'>
|
<div className='max-h-[calc(100vh-320px)] overflow-y-auto'>
|
||||||
<AuthForm
|
{
|
||||||
formSchemas={formSchemas.map((formSchema) => {
|
isLoading && (
|
||||||
return {
|
<div className='flex items-center justify-center'>
|
||||||
...formSchema,
|
<Loading />
|
||||||
name: formSchema.variable,
|
</div>
|
||||||
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
)
|
||||||
}
|
}
|
||||||
}) as FormSchema[]}
|
{
|
||||||
defaultValues={{
|
!isLoading && (
|
||||||
...formSchemasValue,
|
<AuthForm
|
||||||
__authorization_name__: credential?.credential_name,
|
formSchemas={formSchemas.map((formSchema) => {
|
||||||
}}
|
return {
|
||||||
inputClassName='justify-start'
|
...formSchema,
|
||||||
ref={formRef}
|
name: formSchema.variable,
|
||||||
/>
|
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
||||||
|
}
|
||||||
|
}) as FormSchema[]}
|
||||||
|
defaultValues={{
|
||||||
|
...formSchemasValue,
|
||||||
|
__authorization_name__: credential?.credential_name,
|
||||||
|
}}
|
||||||
|
inputClassName='justify-start'
|
||||||
|
ref={formRef}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
!!draftConfig && (
|
!!draftConfig && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ 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'
|
import Authorized from '../model-auth/authorized'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type CredentialPanelProps = {
|
type CredentialPanelProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
@ -49,8 +50,9 @@ const CredentialPanel = ({
|
|||||||
current_credential_name,
|
current_credential_name,
|
||||||
available_credentials,
|
available_credentials,
|
||||||
} = provider.custom_configuration
|
} = provider.custom_configuration
|
||||||
const authorized = current_credential_id && current_credential_name && available_credentials?.every(item => !!item.credential_id)
|
const hasCredential = !!available_credentials?.length
|
||||||
const authRemoved = !!available_credentials?.length && available_credentials?.every(item => !item.credential_id)
|
const authorized = current_credential_id && current_credential_name
|
||||||
|
const authRemoved = hasCredential && !current_credential_id && !current_credential_name
|
||||||
|
|
||||||
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
||||||
const res = await changeModelProviderPriority({
|
const res = await changeModelProviderPriority({
|
||||||
@ -75,21 +77,30 @@ const CredentialPanel = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const credentialLabel = useMemo(() => {
|
const credentialLabel = useMemo(() => {
|
||||||
|
if (!hasCredential)
|
||||||
|
return t('common.model.unAuthorized')
|
||||||
if (authorized)
|
if (authorized)
|
||||||
return current_credential_name
|
return current_credential_name
|
||||||
if (authRemoved)
|
if (authRemoved)
|
||||||
return 'Auth removed'
|
return t('common.model.authRemoved')
|
||||||
return 'Unauthorized'
|
|
||||||
}, [authorized, authRemoved, current_credential_name])
|
return ''
|
||||||
|
}, [authorized, authRemoved, current_credential_name, hasCredential])
|
||||||
|
|
||||||
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={cn(
|
||||||
|
'relative ml-1 w-[120px] shrink-0 rounded-lg border-[0.5px] border-components-panel-border bg-white/[0.18] p-1',
|
||||||
|
authRemoved && 'border-state-destructive-border bg-state-destructive-hover',
|
||||||
|
)}>
|
||||||
<div className='system-xs-medium 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'>
|
||||||
<div
|
<div
|
||||||
className='grow truncate'
|
className={cn(
|
||||||
|
'grow truncate',
|
||||||
|
authRemoved && 'text-text-destructive',
|
||||||
|
)}
|
||||||
title={credentialLabel}
|
title={credentialLabel}
|
||||||
>
|
>
|
||||||
{credentialLabel}
|
{credentialLabel}
|
||||||
@ -98,7 +109,7 @@ const CredentialPanel = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-0.5'>
|
<div className='flex items-center gap-0.5'>
|
||||||
{
|
{
|
||||||
(!authorized || authRemoved) && (
|
!hasCredential && (
|
||||||
<Button
|
<Button
|
||||||
className='grow'
|
className='grow'
|
||||||
size='small'
|
size='small'
|
||||||
@ -106,16 +117,12 @@ const CredentialPanel = ({
|
|||||||
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')}
|
||||||
authRemoved
|
|
||||||
? t('common.operation.config')
|
|
||||||
: t('common.operation.setup')
|
|
||||||
}
|
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
authorized && (
|
(hasCredential || authRemoved) && (
|
||||||
<Authorized
|
<Authorized
|
||||||
provider={provider.provider}
|
provider={provider.provider}
|
||||||
onSetup={onSetup}
|
onSetup={onSetup}
|
||||||
|
|||||||
@ -145,6 +145,8 @@ const translation = {
|
|||||||
addMoreModel: 'Go to settings to add more models',
|
addMoreModel: 'Go to settings to add more models',
|
||||||
settingsLink: 'Model Provider Settings',
|
settingsLink: 'Model Provider Settings',
|
||||||
capabilities: 'MultiModal Capabilities',
|
capabilities: 'MultiModal Capabilities',
|
||||||
|
unAuthorized: 'Unauthorized',
|
||||||
|
authRemoved: 'Auth removed',
|
||||||
},
|
},
|
||||||
menus: {
|
menus: {
|
||||||
status: 'beta',
|
status: 'beta',
|
||||||
|
|||||||
@ -145,6 +145,8 @@ const translation = {
|
|||||||
addMoreModel: '添加更多模型',
|
addMoreModel: '添加更多模型',
|
||||||
settingsLink: '模型设置',
|
settingsLink: '模型设置',
|
||||||
capabilities: '多模态能力',
|
capabilities: '多模态能力',
|
||||||
|
unAuthorized: '未授权',
|
||||||
|
authRemoved: '授权已移除',
|
||||||
},
|
},
|
||||||
menus: {
|
menus: {
|
||||||
status: 'beta',
|
status: 'beta',
|
||||||
|
|||||||
@ -43,3 +43,13 @@ export const useDeleteModelCredential = (providerName: string) => {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useSetModelCredentialDefault = (providerName: string) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (credentialId: string) => post<{ result: string }>(`/workspaces/current/model-providers/${providerName}/credentials/switch`, {
|
||||||
|
body: {
|
||||||
|
credential_id: credentialId,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user