model auth

This commit is contained in:
zxhlyh 2025-08-14 17:59:33 +08:00
parent 4e02abf784
commit 3f57e4a643
8 changed files with 112 additions and 39 deletions

View File

@ -80,13 +80,13 @@ export const useProviderCredentialsAndLoadBalancing = (
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
credentialId?: string,
) => {
const { data: predefinedFormSchemasValue, mutate: mutatePredefined } = useSWR(
const { data: predefinedFormSchemasValue, mutate: mutatePredefined, isLoading: isPredefinedLoading } = useSWR(
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
? `/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`
: null,
fetchModelProviderCredentials,
)
const { data: customFormSchemasValue, mutate: mutateCustomized } = useSWR(
const { data: customFormSchemasValue, mutate: mutateCustomized, isLoading: isCustomizedLoading } = useSWR(
(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}` : ''}`
: null,
@ -122,6 +122,7 @@ export const useProviderCredentialsAndLoadBalancing = (
: customFormSchemasValue
)?.load_balancing,
mutate,
isLoading: isPredefinedLoading || isCustomizedLoading,
}
// as ([Record<string, string | boolean | undefined> | undefined, ModelLoadBalancingConfig | undefined])
}

View File

@ -22,7 +22,10 @@ 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'
import {
useDeleteModelCredential,
useSetModelCredentialDefault,
} from '@/service/use-models'
type AuthorizedProps = {
provider: string
@ -87,6 +90,23 @@ const Authorized = ({
setDoingAction(doing)
}, [])
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 () => {
if (doingActionRef.current)
return
@ -109,9 +129,10 @@ const Authorized = ({
handleSetDoingAction(false)
}
}, [onUpdate, notify, t, handleSetDoingAction])
const handleEdit = useCallback((credential: Credential) => {
const handleOpenSetup = useCallback((credential?: Credential) => {
onSetup(credential)
}, [onSetup])
setMergedIsOpen(false)
}, [onSetup, setMergedIsOpen])
return (
<>
@ -142,10 +163,10 @@ const Authorized = ({
</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',
'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
popupClassName,
)}>
<div className='py-1'>
<div className='max-h-[304px] overflow-y-auto py-1'>
{
!!credentials.length && (
<div className='p-1'>
@ -162,7 +183,8 @@ const Authorized = ({
credential={credential}
disabled={disabled}
onDelete={openConfirm}
onEdit={handleEdit}
onEdit={handleOpenSetup}
onSetDefault={handleSetDefault}
onItemClick={onItemClick}
showSelectedIcon={showItemSelectedIcon}
selectedCredentialId={selectedCredentialId}
@ -176,7 +198,7 @@ const Authorized = ({
<div className='h-[1px] bg-divider-subtle'></div>
<div className='p-2'>
<Button
onClick={() => onSetup()}
onClick={() => handleOpenSetup()}
className='w-full'
>
add api key

View File

@ -13,6 +13,7 @@ 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'
import Button from '@/app/components/base/button'
type ItemProps = {
credential: Credential
@ -33,6 +34,7 @@ const Item = ({
disabled,
onDelete,
onEdit,
onSetDefault,
disableRename,
disableEdit,
disableDelete,
@ -77,6 +79,20 @@ const Item = ({
{
showAction && (
<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 && (
<Tooltip popupContent={t('common.operation.edit')}>

View File

@ -47,6 +47,7 @@ import type {
} from '@/app/components/base/form/types'
import { useModelFormSchemas } from '../model-auth/hooks'
import type { Credential } from '../declarations'
import Loading from '@/app/components/base/loading'
type ModelModalProps = {
provider: ModelProvider
@ -70,6 +71,7 @@ const ModelModal: FC<ModelModalProps> = ({
credentials: formSchemasValue,
loadBalancing: originalConfig,
mutate,
isLoading,
} = useProviderCredentialsAndLoadBalancing(
provider.provider,
configurateMethod,
@ -185,7 +187,7 @@ const ModelModal: FC<ModelModalProps> = ({
)
if (res.result === 'success') {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutate()
// mutate()
onSave()
onCancel()
}
@ -211,21 +213,32 @@ const ModelModal: FC<ModelModalProps> = ({
</div>
<div className='max-h-[calc(100vh-320px)] overflow-y-auto'>
<AuthForm
formSchemas={formSchemas.map((formSchema) => {
return {
...formSchema,
name: formSchema.variable,
showRadioUI: formSchema.type === FormTypeEnum.radio,
}
}) as FormSchema[]}
defaultValues={{
...formSchemasValue,
__authorization_name__: credential?.credential_name,
}}
inputClassName='justify-start'
ref={formRef}
/>
{
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={{
...formSchemasValue,
__authorization_name__: credential?.credential_name,
}}
inputClassName='justify-start'
ref={formRef}
/>
)
}
{
!!draftConfig && (
<>

View File

@ -23,6 +23,7 @@ import { changeModelProviderPriority } from '@/service/common'
import { useToastContext } from '@/app/components/base/toast'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import Authorized from '../model-auth/authorized'
import cn from '@/utils/classnames'
type CredentialPanelProps = {
provider: ModelProvider
@ -49,8 +50,9 @@ const CredentialPanel = ({
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 hasCredential = !!available_credentials?.length
const authorized = current_credential_id && current_credential_name
const authRemoved = hasCredential && !current_credential_id && !current_credential_name
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
const res = await changeModelProviderPriority({
@ -75,21 +77,30 @@ const CredentialPanel = ({
}
}
const credentialLabel = useMemo(() => {
if (!hasCredential)
return t('common.model.unAuthorized')
if (authorized)
return current_credential_name
if (authRemoved)
return 'Auth removed'
return 'Unauthorized'
}, [authorized, authRemoved, current_credential_name])
return t('common.model.authRemoved')
return ''
}, [authorized, authRemoved, current_credential_name, hasCredential])
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={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='grow truncate'
className={cn(
'grow truncate',
authRemoved && 'text-text-destructive',
)}
title={credentialLabel}
>
{credentialLabel}
@ -98,7 +109,7 @@ const CredentialPanel = ({
</div>
<div className='flex items-center gap-0.5'>
{
(!authorized || authRemoved) && (
!hasCredential && (
<Button
className='grow'
size='small'
@ -106,16 +117,12 @@ const CredentialPanel = ({
variant={!authorized ? 'secondary-accent' : 'secondary'}
>
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
{
authRemoved
? t('common.operation.config')
: t('common.operation.setup')
}
{t('common.operation.setup')}
</Button>
)
}
{
authorized && (
(hasCredential || authRemoved) && (
<Authorized
provider={provider.provider}
onSetup={onSetup}

View File

@ -145,6 +145,8 @@ const translation = {
addMoreModel: 'Go to settings to add more models',
settingsLink: 'Model Provider Settings',
capabilities: 'MultiModal Capabilities',
unAuthorized: 'Unauthorized',
authRemoved: 'Auth removed',
},
menus: {
status: 'beta',

View File

@ -145,6 +145,8 @@ const translation = {
addMoreModel: '添加更多模型',
settingsLink: '模型设置',
capabilities: '多模态能力',
unAuthorized: '未授权',
authRemoved: '授权已移除',
},
menus: {
status: 'beta',

View File

@ -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,
},
}),
})
}