From 2e28a64d380712706d87ee80ef33afd7e8ad1572 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Fri, 15 Aug 2025 18:34:12 +0800 Subject: [PATCH] model auth --- .../model-provider-page/declarations.ts | 30 +++ .../model-provider-page/hooks.ts | 3 + .../add-credential-in-load-balancing.tsx | 26 +- .../model-auth/add-custom-model.tsx | 78 ++++++ .../model-auth/add-model.tsx | 76 ------ .../model-auth/authorized/authorized-item.tsx | 90 +++++++ .../{item.tsx => credential-item.tsx} | 10 +- .../model-auth/authorized/index.tsx | 161 +++++-------- .../model-auth/config-provider.tsx | 82 +++++++ .../model-auth/hooks/index.ts | 5 + .../model-auth/hooks/use-auth-service.ts | 57 +++++ .../model-auth/hooks/use-auth.ts | 153 ++++++++++++ .../model-auth/hooks/use-credential-status.ts | 24 ++ .../model-auth/hooks/use-custom-models.ts | 9 + .../use-model-form-schemas.ts} | 4 +- .../model-provider-page/model-auth/index.tsx | 5 + .../switch-credential-in-load-balancing.tsx | 104 ++++++-- .../model-provider-page/model-modal/index.tsx | 223 +++++------------- .../provider-added-card/credential-panel.tsx | 49 +--- .../provider-added-card/index.tsx | 5 - .../provider-added-card/model-list.tsx | 23 +- .../model-load-balancing-configs.tsx | 16 +- .../model-load-balancing-modal.tsx | 24 +- .../model-provider-page/utils.ts | 5 +- web/context/modal-context.tsx | 3 + web/service/use-models.ts | 121 ++++++++-- 26 files changed, 917 insertions(+), 469 deletions(-) create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx delete mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/add-model.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/authorized/authorized-item.tsx rename web/app/components/header/account-setting/model-provider-page/model-auth/authorized/{item.tsx => credential-item.tsx} (95%) create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/hooks/index.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth-service.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-credential-status.ts create mode 100644 web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts rename web/app/components/header/account-setting/model-provider-page/model-auth/{hooks.ts => hooks/use-model-form-schemas.ts} (96%) diff --git a/web/app/components/header/account-setting/model-provider-page/declarations.ts b/web/app/components/header/account-setting/model-provider-page/declarations.ts index 575c96e1ed..16ac26c17a 100644 --- a/web/app/components/header/account-setting/model-provider-page/declarations.ts +++ b/web/app/components/header/account-setting/model-provider-page/declarations.ts @@ -186,6 +186,22 @@ export type Credential = { credential_name: string } +export type CustomModel = { + model: string + model_type: ModelTypeEnum +} + +export type CustomModelCredential = CustomModel & { + credentials?: Record + available_model_credentials?: Credential[] + current_credential_id?: string +} + +export type CredentialWithModel = Credential & { + model: string + model_type: ModelTypeEnum +} + export type ModelProvider = { provider: string label: TypeWithI18N @@ -215,6 +231,7 @@ export type ModelProvider = { current_credential_id?: string current_credential_name?: string available_credentials?: Credential[] + custom_models?: CustomModelCredential[] } system_configuration: { enabled: boolean @@ -280,9 +297,22 @@ export type ModelLoadBalancingConfigEntry = { in_cooldown?: boolean /** cooldown time (in seconds) */ ttl?: number + credential_id?: string } export type ModelLoadBalancingConfig = { enabled: boolean configs: ModelLoadBalancingConfigEntry[] } + +export type ProviderCredential = { + credentials: Record + name: string + credential_id: string +} + +export type ModelCredential = { + credentials: Record + load_balancing: ModelLoadBalancingConfig + available_credentials: Credential[] +} diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index c9836ae7e6..5527e283cc 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -9,6 +9,7 @@ import { useContext } from 'use-context-selector' import type { Credential, CustomConfigurationModelFixedFields, + CustomModel, DefaultModel, DefaultModelResponse, Model, @@ -354,6 +355,7 @@ export const useModelModalHandler = () => { configurationMethod: ConfigurationMethodEnum, CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, credential?: Credential, + model?: CustomModel, ) => { setShowModelModal({ payload: { @@ -361,6 +363,7 @@ export const useModelModalHandler = () => { currentConfigurationMethod: configurationMethod, currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields, credential, + model, }, onSaveCallback: () => { handleRefreshModel(provider, configurationMethod, CustomConfigurationModelFixedFields) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/add-credential-in-load-balancing.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/add-credential-in-load-balancing.tsx index b913f7284d..d1c366f4f1 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/add-credential-in-load-balancing.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/add-credential-in-load-balancing.tsx @@ -3,13 +3,21 @@ import { useCallback, } from 'react' import { RiAddLine } from '@remixicon/react' -import { - AuthCategory, - Authorized, -} from '@/app/components/plugins/plugin-auth' +import { Authorized } from '@/app/components/header/account-setting/model-provider-page/model-auth' import cn from '@/utils/classnames' +import type { + Credential, + ModelProvider, +} from '@/app/components/header/account-setting/model-provider-page/declarations' -const AddCredentialInLoadBalancing = () => { +type AddCredentialInLoadBalancingProps = { + provider: ModelProvider + onSetup: (credential?: Credential) => void +} +const AddCredentialInLoadBalancing = ({ + provider, + onSetup, +}: AddCredentialInLoadBalancingProps) => { const renderTrigger = useCallback((open?: boolean) => { return (
{ return ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx new file mode 100644 index 0000000000..36908e0260 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx @@ -0,0 +1,78 @@ +import { + memo, + useCallback, + useMemo, +} from 'react' +import { useTranslation } from 'react-i18next' +import { + RiAddCircleFill, +} from '@remixicon/react' +import { + Button, +} from '@/app/components/base/button' +import type { + CustomConfigurationModelFixedFields, + CustomModelCredential, + ModelProvider, +} 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 { useAuth } from './hooks' +import cn from '@/utils/classnames' + +type AddCustomModelProps = { + provider: ModelProvider, + configurationMethod: ConfigurationMethodEnum, + currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, + models: CustomModelCredential[] +} +const AddCustomModel = ({ + provider, + configurationMethod, + currentCustomConfigurationModelFixedFields, + models, +}: AddCustomModelProps) => { + const { t } = useTranslation() + const noModels = !models.length + const { + handleOpenModal, + } = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields) + const handleClick = useCallback(() => { + if (noModels) + handleOpenModal() + }, [handleOpenModal, noModels]) + const ButtonComponent = useMemo(() => { + return ( + + ) + }, [handleClick, noModels]) + + const renderTrigger = useCallback(() => { + return ButtonComponent + }, [ButtonComponent]) + + if (noModels) + return ButtonComponent + + return ( + ({ + model, + credentials: model.available_model_credentials ?? [], + }))} + renderTrigger={renderTrigger} + /> + ) +} + +export default memo(AddCustomModel) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/add-model.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/add-model.tsx deleted file mode 100644 index fcd4272a27..0000000000 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/add-model.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { - memo, - useState, -} from 'react' -import { useTranslation } from 'react-i18next' -import { - RiAddCircleFill, - RiAddLine, -} from '@remixicon/react' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import Tooltip from '@/app/components/base/tooltip' - -const AddModel = () => { - const { t } = useTranslation() - const [open, setOpen] = useState(false) - - return ( - - - - - -
-
-
-
-
- chat-finetune-01 -
- - - -
-
-
- - {t('common.modelProvider.addModel')} -
-
-
-
- ) -} - -export default memo(AddModel) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/authorized-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/authorized-item.tsx new file mode 100644 index 0000000000..f95161e46b --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/authorized-item.tsx @@ -0,0 +1,90 @@ +import { + memo, + useCallback, +} from 'react' +import { RiAddLine } from '@remixicon/react' +import CredentialItem from './credential-item' +import type { + Credential, + CustomModel, +} from '../../declarations' +import Button from '@/app/components/base/button' +import Tooltip from '@/app/components/base/tooltip' + +type AuthorizedItemProps = { + model?: CustomModel + disabled?: boolean + onDelete?: (id: string) => void + onEdit?: (model?: CustomModel, credential?: Credential) => void + onSetDefault?: (id: string) => void + onItemClick?: (id: string) => void + showItemSelectedIcon?: boolean + selectedCredentialId?: string + disableSetDefault?: boolean + credentials: Credential[] +} +export const AuthorizedItem = ({ + model, + credentials, + disabled, + onDelete, + onEdit, + onSetDefault, + onItemClick, + showItemSelectedIcon, + selectedCredentialId, + disableSetDefault, +}: AuthorizedItemProps) => { + const handleEdit = useCallback((credential?: Credential) => { + onEdit?.(model, credential) + }, [onEdit, model]) + return ( +
+ { + model && ( +
+
+
+ {model.model} +
+ + + +
+ ) + } + { + credentials.map(credential => ( + + )) + } +
+ ) +} + +export default memo(AuthorizedItem) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/item.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx similarity index 95% rename from web/app/components/header/account-setting/model-provider-page/model-auth/authorized/item.tsx rename to web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx index b4b77a2493..f0f872dd84 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx @@ -15,11 +15,11 @@ import cn from '@/utils/classnames' import type { Credential } from '../../declarations' import Button from '@/app/components/base/button' -type ItemProps = { +type CredentialItemProps = { credential: Credential disabled?: boolean onDelete?: (id: string) => void - onEdit?: (credential: Credential) => void + onEdit?: (credential?: Credential) => void onSetDefault?: (id: string) => void disableRename?: boolean disableEdit?: boolean @@ -29,7 +29,7 @@ type ItemProps = { showSelectedIcon?: boolean selectedCredentialId?: string } -const Item = ({ +const CredentialItem = ({ credential, disabled, onDelete, @@ -42,7 +42,7 @@ const Item = ({ onItemClick, showSelectedIcon, selectedCredentialId, -}: ItemProps) => { +}: CredentialItemProps) => { const { t } = useTranslation() const showAction = useMemo(() => { return !(disableRename && disableEdit && disableDelete && disableSetDefault) @@ -131,4 +131,4 @@ const Item = ({ ) } -export default memo(Item) +export default memo(CredentialItem) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx index e20fd58216..6063ad02be 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx @@ -1,7 +1,6 @@ import { memo, useCallback, - useRef, useState, } from 'react' import { @@ -19,17 +18,25 @@ import type { 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, - useSetModelCredentialDefault, -} from '@/service/use-models' +import type { + ConfigurationMethodEnum, + Credential, + CustomConfigurationModelFixedFields, + CustomModel, + ModelProvider, +} from '../../declarations' +import { useAuth } from '../hooks' +import AuthorizedItem from './authorized-item' type AuthorizedProps = { - provider: string - credentials: Credential[] + provider: ModelProvider, + configurationMethod: ConfigurationMethodEnum, + currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, + items: { + model?: CustomModel + credentials: Credential[] + }[] + selectedCredential?: Credential disabled?: boolean renderTrigger?: (open?: boolean) => React.ReactNode isOpen?: boolean @@ -40,13 +47,15 @@ type AuthorizedProps = { popupClassName?: string onItemClick?: (id: string) => void showItemSelectedIcon?: boolean - selectedCredentialId?: string onUpdate?: () => void - onSetup: (credential?: Credential) => void + disableSetDefault?: boolean } const Authorized = ({ provider, - credentials, + configurationMethod, + currentCustomConfigurationModelFixedFields, + items, + selectedCredential, disabled, renderTrigger, isOpen, @@ -57,12 +66,10 @@ const Authorized = ({ popupClassName, onItemClick, showItemSelectedIcon, - selectedCredentialId, onUpdate, - onSetup, + disableSetDefault, }: AuthorizedProps) => { const { t } = useTranslation() - const { notify } = useToastContext() const [isLocalOpen, setIsLocalOpen] = useState(false) const mergedIsOpen = isOpen ?? isLocalOpen const setMergedIsOpen = useCallback((open: boolean) => { @@ -71,68 +78,20 @@ const Authorized = ({ setIsLocalOpen(open) }, [onOpenChange]) - const pendingOperationCredentialId = useRef(null) - const [deleteCredentialId, setDeleteCredentialId] = useState(null) - const openConfirm = useCallback((credentialId?: string) => { - if (credentialId) - pendingOperationCredentialId.current = credentialId + const { + openConfirmDelete, + closeConfirmDelete, + doingAction, + handleActiveCredential, + handleConfirmDelete, + deleteCredentialId, + handleOpenModal, + } = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields, onUpdate) - 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 { 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 - 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 handleOpenSetup = useCallback((credential?: Credential) => { - onSetup(credential) + const handleEdit = useCallback((model?: CustomModel, credential?: Credential) => { + handleOpenModal(model, credential) setMergedIsOpen(false) - }, [onSetup, setMergedIsOpen]) + }, [handleOpenModal, setMergedIsOpen]) return ( <> @@ -166,39 +125,29 @@ const Authorized = ({ 'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg', popupClassName, )}> -
+
{ - !!credentials.length && ( -
-
- API Keys -
- { - credentials.map(credential => ( - - )) - } -
- ) + items.map((item, index) => ( + + )) }
+ ) + }, [handleClick, authorized]) + + if (!hasCredential) + return ButtonComponent + + return ( + + ) +} + +export default memo(ConfigProvider) diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/index.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/index.ts new file mode 100644 index 0000000000..738fe8a6ba --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/index.ts @@ -0,0 +1,5 @@ +export * from './use-model-form-schemas' +export * from './use-credential-status' +export * from './use-custom-models' +export * from './use-auth' +export * from './use-auth-service' diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth-service.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth-service.ts new file mode 100644 index 0000000000..0f5de9bde9 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth-service.ts @@ -0,0 +1,57 @@ +import { useCallback } from 'react' +import { + useActiveModelCredential, + useActiveProviderCredential, + useAddModelCredential, + useAddProviderCredential, + useDeleteModelCredential, + useDeleteProviderCredential, + useEditModelCredential, + useEditProviderCredential, + useGetModelCredential, + useGetProviderCredential, +} from '@/service/use-models' +import type { + CustomModel, +} from '@/app/components/header/account-setting/model-provider-page/declarations' + +export const useGetCredential = (provider: string, credentialId?: string, model?: CustomModel, configFrom?: string) => { + const providerData = useGetProviderCredential(!model && !!credentialId, provider, credentialId) + const modelData = useGetModelCredential(!!model && !!credentialId, provider, credentialId, model?.model, model?.model_type, configFrom) + return model ? modelData : providerData +} + +export const useAuthService = (provider: string) => { + const { mutateAsync: addProviderCredential } = useAddProviderCredential(provider) + const { mutateAsync: editProviderCredential } = useEditProviderCredential(provider) + const { mutateAsync: deleteProviderCredential } = useDeleteProviderCredential(provider) + const { mutateAsync: activeProviderCredential } = useActiveProviderCredential(provider) + + const { mutateAsync: addModelCredential } = useAddModelCredential(provider) + const { mutateAsync: activeModelCredential } = useActiveModelCredential(provider) + const { mutateAsync: deleteModelCredential } = useDeleteModelCredential(provider) + const { mutateAsync: editModelCredential } = useEditModelCredential(provider) + + const getAddCredentialService = useCallback((isModel: boolean) => { + return isModel ? addModelCredential : addProviderCredential + }, [addModelCredential, addProviderCredential]) + + const getEditCredentialService = useCallback((isModel: boolean) => { + return isModel ? editModelCredential : editProviderCredential + }, [editModelCredential, editProviderCredential]) + + const getDeleteCredentialService = useCallback((isModel: boolean) => { + return isModel ? deleteModelCredential : deleteProviderCredential + }, [deleteModelCredential, deleteProviderCredential]) + + const getActiveCredentialService = useCallback((isModel: boolean) => { + return isModel ? activeModelCredential : activeProviderCredential + }, [activeModelCredential, activeProviderCredential]) + + return { + getAddCredentialService, + getEditCredentialService, + getDeleteCredentialService, + getActiveCredentialService, + } +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts new file mode 100644 index 0000000000..f33901b1f9 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts @@ -0,0 +1,153 @@ +import { + useCallback, + useRef, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { useToastContext } from '@/app/components/base/toast' +import { useAuthService } from './use-auth-service' +import type { + ConfigurationMethodEnum, + Credential, + CustomConfigurationModelFixedFields, + CustomModel, + ModelProvider, +} from '../../declarations' +import { + useModelModalHandler, + useRefreshModel, +} from '@/app/components/header/account-setting/model-provider-page/hooks' + +export const useAuth = ( + provider: ModelProvider, + configurationMethod: ConfigurationMethodEnum, + currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, + onUpdate?: () => void, +) => { + const { t } = useTranslation() + const { notify } = useToastContext() + const { + getDeleteCredentialService, + getActiveCredentialService, + getEditCredentialService, + getAddCredentialService, + } = useAuthService(provider.provider) + const handleOpenModelModal = useModelModalHandler() + const { handleRefreshModel } = useRefreshModel() + const pendingOperationCredentialId = useRef(null) + const pendingOperationModel = useRef(null) + const [deleteCredentialId, setDeleteCredentialId] = useState(null) + const openConfirmDelete = useCallback((credentialId?: string, model?: CustomModel) => { + if (credentialId) + pendingOperationCredentialId.current = credentialId + if (model) + pendingOperationModel.current = model + + setDeleteCredentialId(pendingOperationCredentialId.current) + }, []) + const closeConfirmDelete = 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 handleActiveCredential = useCallback(async (id: string, model?: CustomModel) => { + if (doingActionRef.current) + return + try { + handleSetDoingAction(true) + await getActiveCredentialService(!!model)({ + credential_id: id, + model: model?.model, + model_type: model?.model_type, + }) + notify({ + type: 'success', + message: t('common.api.actionSuccess'), + }) + onUpdate?.() + } + finally { + handleSetDoingAction(false) + } + }, [getActiveCredentialService, onUpdate, notify, t, handleSetDoingAction]) + const handleConfirmDelete = useCallback(async () => { + if (doingActionRef.current) + return + if (!pendingOperationCredentialId.current) { + setDeleteCredentialId(null) + return + } + try { + handleSetDoingAction(true) + await getDeleteCredentialService(!!pendingOperationModel.current)({ + credential_id: pendingOperationCredentialId.current, + model: pendingOperationModel.current?.model, + model_type: pendingOperationModel.current?.model_type, + }) + notify({ + type: 'success', + message: t('common.api.actionSuccess'), + }) + onUpdate?.() + handleRefreshModel(provider, configurationMethod, undefined) + setDeleteCredentialId(null) + pendingOperationCredentialId.current = null + } + finally { + handleSetDoingAction(false) + } + }, [onUpdate, notify, t, handleSetDoingAction, getDeleteCredentialService]) + const handleAddCredential = useCallback((model?: CustomModel) => { + if (model) + pendingOperationModel.current = model + }, []) + const handleSaveCredential = useCallback(async (payload: Record) => { + if (doingActionRef.current) + return + try { + handleSetDoingAction(true) + + let res: { result?: string } = {} + if (payload.credential_id) + res = await getEditCredentialService(!!payload.model)(payload as any) + else + res = await getAddCredentialService(!!payload.model)(payload as any) + + if (res.result === 'success') { + notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) + onUpdate?.() + } + } + finally { + handleSetDoingAction(false) + } + }, [onUpdate, notify, t, handleSetDoingAction, getEditCredentialService, getAddCredentialService]) + const handleOpenModal = useCallback((model?: CustomModel, credential?: Credential) => { + handleOpenModelModal( + provider, + configurationMethod, + currentCustomConfigurationModelFixedFields, + credential, + model, + ) + }, [handleOpenModelModal, provider, configurationMethod, currentCustomConfigurationModelFixedFields]) + + return { + pendingOperationCredentialId, + pendingOperationModel, + openConfirmDelete, + closeConfirmDelete, + doingAction, + handleActiveCredential, + handleConfirmDelete, + handleAddCredential, + deleteCredentialId, + handleSaveCredential, + handleOpenModal, + } +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-credential-status.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-credential-status.ts new file mode 100644 index 0000000000..c83ba1cf80 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-credential-status.ts @@ -0,0 +1,24 @@ +import { useMemo } from 'react' +import type { + ModelProvider, +} from '../../declarations' + +export const useCredentialStatus = (provider: ModelProvider) => { + const { + current_credential_id, + current_credential_name, + available_credentials, + } = provider.custom_configuration + const hasCredential = !!available_credentials?.length + const authorized = current_credential_id && current_credential_name + const authRemoved = hasCredential && !current_credential_id && !current_credential_name + + return useMemo(() => ({ + hasCredential, + authorized, + authRemoved, + current_credential_id, + current_credential_name, + available_credentials, + }), [hasCredential, authorized, authRemoved, current_credential_id, current_credential_name, available_credentials]) +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts new file mode 100644 index 0000000000..f3b50f3f49 --- /dev/null +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts @@ -0,0 +1,9 @@ +import type { + ModelProvider, +} from '../../declarations' + +export const useCustomModels = (provider: ModelProvider) => { + const { custom_models } = provider.custom_configuration + + return custom_models || [] +} diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts similarity index 96% rename from web/app/components/header/account-setting/model-provider-page/model-auth/hooks.ts rename to web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts index 94db6cfc8d..463c905959 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts @@ -3,11 +3,11 @@ import { useTranslation } from 'react-i18next' import type { ModelLoadBalancingConfig, ModelProvider, -} from '../declarations' +} from '../../declarations' import { genModelNameFormSchema, genModelTypeFormSchema, -} from '../utils' +} from '../../utils' import { FormTypeEnum } from '@/app/components/base/form/types' export const useModelFormSchemas = ( diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/index.tsx index e69de29bb2..e33e30da3a 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/index.tsx @@ -0,0 +1,5 @@ +export { default as Authorized } from './authorized' +export { default as SwitchCredentialInLoadBalancing } from './switch-credential-in-load-balancing' +export { default as AddCredentialInLoadBalancing } from './add-credential-in-load-balancing' +export { default as AddCustomModel } from './add-custom-model' +export { default as ConfigProvider } from './config-provider' diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx index bb5b8bd62e..e9e1f16358 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx @@ -1,45 +1,107 @@ +import type { Dispatch, SetStateAction } from 'react' import { memo, useCallback, } from 'react' +import { useTranslation } from 'react-i18next' import { RiArrowDownSLine } from '@remixicon/react' import Button from '@/app/components/base/button' -import { - AuthCategory, - Authorized, -} from '@/app/components/plugins/plugin-auth' import Indicator from '@/app/components/header/indicator' import Badge from '@/app/components/base/badge' +import Authorized from './authorized' +import type { + Credential, + ModelLoadBalancingConfig, + ModelProvider, +} from '../declarations' +import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useCredentialStatus } from './hooks' +import { useModelModalHandler } from '../hooks' +import cn from '@/utils/classnames' + +type SwitchCredentialInLoadBalancingProps = { + provider: ModelProvider + draftConfig?: ModelLoadBalancingConfig + setDraftConfig: Dispatch> +} +const SwitchCredentialInLoadBalancing = ({ + provider, + draftConfig, + setDraftConfig, +}: SwitchCredentialInLoadBalancingProps) => { + const { t } = useTranslation() + const { + available_credentials, + current_credential_name, + } = useCredentialStatus(provider) + const handleOpenModal = useModelModalHandler() + console.log(draftConfig, 'draftConfig') + + const handleSetup = useCallback((credential?: Credential) => { + handleOpenModal(provider, ConfigurationMethodEnum.predefinedModel, undefined, credential) + }, [handleOpenModal, provider]) + + const handleItemClick = useCallback((id: string) => { + setDraftConfig((prev) => { + if (!prev) + return prev + const newConfigs = [...prev.configs] + const index = newConfigs.findIndex(config => config.name === '__inherit__') + const inheritConfig = newConfigs[index] + const modifiedConfig = inheritConfig ? { + ...inheritConfig, + credential_id: id, + } : { + name: '__inherit__', + credential_id: id, + credentials: {}, + } + newConfigs.splice(index, 1, modifiedConfig) + return { + ...prev, + configs: newConfigs, + } + }) + }, [setDraftConfig]) -const SwitchCredentialInLoadBalancing = () => { const renderTrigger = useCallback(() => { + const selectedCredentialId = draftConfig?.configs.find(config => config.name === '__inherit__')?.credential_id + const selectedCredential = available_credentials?.find(credential => credential.credential_id === selectedCredentialId) + const name = selectedCredential?.credential_name || current_credential_name + const authRemoved = !!selectedCredentialId && !selectedCredential return ( ) - }, []) + }, [current_credential_name, t, draftConfig, available_credentials]) return ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 4c9652da8f..937fe6a9ae 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -2,34 +2,20 @@ import type { FC } from 'react' import { memo, useCallback, - useEffect, - useMemo, useRef, - useState, } from 'react' import { useTranslation } from 'react-i18next' import type { CustomConfigurationModelFixedFields, - ModelLoadBalancingConfig, - ModelLoadBalancingConfigEntry, ModelProvider, } from '../declarations' import { ConfigurationMethodEnum, - CustomConfigurationStatusEnum, FormTypeEnum, } from '../declarations' -import { - genModelNameFormSchema, - genModelTypeFormSchema, - removeCredentials, - saveCredentials, -} from '../utils' import { useLanguage, - useProviderCredentialsAndLoadBalancing, } from '../hooks' -import ModelLoadBalancingConfigs from '../provider-added-card/model-load-balancing-configs' import Button from '@/app/components/base/button' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' @@ -37,7 +23,6 @@ import { PortalToFollowElem, PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' -import { useToastContext } from '@/app/components/base/toast' import Confirm from '@/app/components/base/confirm' import { useAppContext } from '@/context/app-context' import AuthForm from '@/app/components/base/form/form-scenarios/auth' @@ -46,20 +31,29 @@ import type { FormSchema, } from '@/app/components/base/form/types' import { useModelFormSchemas } from '../model-auth/hooks' -import type { Credential } from '../declarations' +import type { + Credential, + CustomModel, +} from '../declarations' import Loading from '@/app/components/base/loading' +import { + useAuth, + useGetCredential, +} from '@/app/components/header/account-setting/model-provider-page/model-auth/hooks' type ModelModalProps = { provider: ModelProvider + model?: CustomModel + credential?: Credential configurateMethod: ConfigurationMethodEnum currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields - credential?: Credential onCancel: () => void onSave: () => void } const ModelModal: FC = ({ provider, + model, configurateMethod, currentCustomConfigurationModelFixedFields, credential, @@ -68,134 +62,62 @@ const ModelModal: FC = ({ }) => { const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel const { - credentials: formSchemasValue, - loadBalancing: originalConfig, - mutate, isLoading, - } = useProviderCredentialsAndLoadBalancing( - provider.provider, - configurateMethod, - providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active, - currentCustomConfigurationModelFixedFields, - credential?.credential_id, - ) + data: credentialData = {}, + } = useGetCredential(provider.provider, credential?.credential_id, model) + const { + handleSaveCredential, + handleConfirmDelete, + deleteCredentialId, + closeConfirmDelete, + openConfirmDelete, + doingAction, + } = useAuth(provider, configurateMethod, currentCustomConfigurationModelFixedFields, onSave) + const { + credentials: formSchemasValue, + } = credentialData as any + const { isCurrentWorkspaceManager } = useAppContext() const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager const { t } = useTranslation() - const { notify } = useToastContext() const language = useLanguage() - const [loading, setLoading] = useState(false) - const [showConfirm, setShowConfirm] = useState(false) - - const [draftConfig, setDraftConfig] = useState() - const originalConfigMap = useMemo(() => { - if (!originalConfig) - return {} - return originalConfig?.configs.reduce((prev, config) => { - if (config.id) - prev[config.id] = config - return prev - }, {} as Record) - }, [originalConfig]) - useEffect(() => { - if (originalConfig && !draftConfig) - setDraftConfig(originalConfig) - }, [draftConfig, originalConfig]) - - const { formSchemas } = useModelFormSchemas(provider, providerFormSchemaPredefined, draftConfig) + const { formSchemas } = useModelFormSchemas(provider, providerFormSchemaPredefined) const formRef = useRef(null) - const extendedSecretFormSchemas = useMemo( - () => - (providerFormSchemaPredefined - ? provider.provider_credential_schema.credential_form_schemas - : [ - genModelTypeFormSchema(provider.supported_model_types), - genModelNameFormSchema(provider.model_credential_schema?.model), - ...provider.model_credential_schema.credential_form_schemas, - ]).filter(({ type }) => type === FormTypeEnum.secretInput), - [ - provider.model_credential_schema?.credential_form_schemas, - provider.model_credential_schema?.model, - provider.provider_credential_schema?.credential_form_schemas, - provider.supported_model_types, - providerFormSchemaPredefined, - ], - ) + const handleSave = useCallback(async () => { + const { + isCheckValidated, + values, + } = formRef.current?.getFormValues({ + needCheckValidatedValues: true, + needTransformWhenSecretFieldIsPristine: true, + }) || { isCheckValidated: false, values: {} } + if (!isCheckValidated) + return - const encodeConfigEntrySecretValues = useCallback((entry: ModelLoadBalancingConfigEntry) => { - const result = { ...entry } - extendedSecretFormSchemas.forEach(({ variable }) => { - if (entry.id && result.credentials[variable] === originalConfigMap[entry.id]?.credentials?.[variable]) - result.credentials[variable] = '[__HIDDEN__]' - }) - return result - }, [extendedSecretFormSchemas, originalConfigMap]) - - const handleSave = async () => { - try { - setLoading(true) - const { - isCheckValidated, - values, - } = formRef.current?.getFormValues({ - needCheckValidatedValues: true, - needTransformWhenSecretFieldIsPristine: true, - }) || { isCheckValidated: false, values: {} } - if (!isCheckValidated) - return - - const res = await saveCredentials( - providerFormSchemaPredefined, - provider.provider, - values, - { - ...draftConfig, - enabled: Boolean(draftConfig?.enabled), - configs: draftConfig?.configs.map(encodeConfigEntrySecretValues) || [], - }, - ) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - mutate() - onSave() - onCancel() - } + const { + __authorization_name__, + __model_name, + __model_type, + ...rest + } = values + if (__model_name && __model_type) { + handleSaveCredential({ + credential_id: credential?.credential_id, + credentials: rest, + name: __authorization_name__, + model: __model_name, + model_type: __model_type, + }) } - finally { - setLoading(false) + else { + handleSaveCredential({ + credential_id: credential?.credential_id, + credentials: rest, + name: __authorization_name__, + }) } - } - - const handleRemove = async () => { - try { - setLoading(true) - const { - isCheckValidated, - values, - } = formRef.current?.getFormValues({ - needCheckValidatedValues: true, - needTransformWhenSecretFieldIsPristine: true, - }) || { isCheckValidated: false, values: {} } - if (!isCheckValidated) - return - const res = await removeCredentials( - providerFormSchemaPredefined, - provider.provider, - values, - credential?.credential_id, - ) - if (res.result === 'success') { - notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - // mutate() - onSave() - onCancel() - } - } - finally { - setLoading(false) - } - } + }, [handleSaveCredential, credential?.credential_id, model]) const renderTitlePrefix = () => { const prefix = isEditMode ? t('common.operation.setup') : t('common.operation.add') @@ -239,20 +161,6 @@ const ModelModal: FC = ({ /> ) } - { - !!draftConfig && ( - <> -
- - - ) - }
@@ -278,7 +186,7 @@ const ModelModal: FC = ({ variant='warning' size='large' className='mr-2' - onClick={() => setShowConfirm(true)} + onClick={() => openConfirmDelete(credential?.credential_id, model)} > {t('common.operation.remove')} @@ -295,11 +203,7 @@ const ModelModal: FC = ({ size='large' variant='primary' onClick={handleSave} - disabled={ - loading - || (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2) - } - + disabled={isLoading || doingAction} > {t('common.operation.save')} @@ -322,12 +226,13 @@ const ModelModal: FC = ({
{ - showConfirm && ( + deleteCredentialId && ( setShowConfirm(false)} - onConfirm={handleRemove} + isDisabled={doingAction} + onCancel={closeConfirmDelete} + onConfirm={handleConfirmDelete} /> ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx index 85d013ba45..02a2b83934 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx @@ -1,8 +1,6 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { RiEqualizer2Line } from '@remixicon/react' import type { - Credential, ModelProvider, } from '../declarations' import { @@ -18,22 +16,18 @@ import PrioritySelector from './priority-selector' import PriorityUseTip from './priority-use-tip' import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './index' import Indicator from '@/app/components/header/indicator' -import Button from '@/app/components/base/button' 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' +import { useCredentialStatus } from '@/app/components/header/account-setting/model-provider-page/model-auth/hooks' +import { ConfigProvider } from '@/app/components/header/account-setting/model-provider-page/model-auth' type CredentialPanelProps = { provider: ModelProvider - onSetup: (credential?: Credential) => void - onUpdate: () => void } const CredentialPanel = ({ provider, - onSetup, - onUpdate, }: CredentialPanelProps) => { const { t } = useTranslation() const { notify } = useToastContext() @@ -46,13 +40,11 @@ const CredentialPanel = ({ const isCustomConfigured = customConfig.status === CustomConfigurationStatusEnum.active const configurateMethods = provider.configurate_methods const { - current_credential_id, + hasCredential, + authorized, + authRemoved, current_credential_name, - available_credentials, - } = provider.custom_configuration - const hasCredential = !!available_credentials?.length - const authorized = current_credential_id && current_credential_name - const authRemoved = hasCredential && !current_credential_id && !current_credential_name + } = useCredentialStatus(provider) const handleChangePriority = async (key: PreferredProviderTypeEnum) => { const res = await changeModelProviderPriority({ @@ -108,31 +100,10 @@ const CredentialPanel = ({
- { - !hasCredential && ( - - ) - } - { - (hasCredential || authRemoved) && ( - - ) - } + { systemConfig.enabled && isCustomConfigured && ( = ({ getModelList(v.payload) }) - const { handleRefreshModel } = useRefreshModel() - return (
= ({ { showCredential && ( onOpenModal(ConfigurationMethodEnum.predefinedModel, undefined, credential)} - onUpdate={() => handleRefreshModel(provider, ConfigurationMethodEnum.predefinedModel, undefined)} provider={provider} /> ) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index 699be6edda..9bcf226064 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -5,6 +5,7 @@ import { RiArrowRightSLine, } from '@remixicon/react' import type { + Credential, CustomConfigurationModelFixedFields, ModelItem, ModelProvider, @@ -13,10 +14,11 @@ import { ConfigurationMethodEnum, } from '../declarations' // import Tab from './tab' -import AddModelButton from './add-model-button' import ModelListItem from './model-list-item' import { useModalContextSelector } from '@/context/modal-context' import { useAppContext } from '@/context/app-context' +import { useCustomModels } from '@/app/components/header/account-setting/model-provider-page/model-auth/hooks' +import { AddCustomModel } from '@/app/components/header/account-setting/model-provider-page/model-auth' type ModelListProps = { provider: ModelProvider @@ -36,11 +38,12 @@ const ModelList: FC = ({ const configurativeMethods = provider.configurate_methods.filter(method => method !== ConfigurationMethodEnum.fetchFromRemote) const { isCurrentWorkspaceManager } = useAppContext() const isConfigurable = configurativeMethods.includes(ConfigurationMethodEnum.customizableModel) - + const customModels = useCustomModels(provider) const setShowModelLoadBalancingModal = useModalContextSelector(state => state.setShowModelLoadBalancingModal) - const onModifyLoadBalancing = useCallback((model: ModelItem) => { + const onModifyLoadBalancing = useCallback((model: ModelItem, credential?: Credential) => { setShowModelLoadBalancingModal({ provider, + credential, model: model!, open: !!model, onClose: () => setShowModelLoadBalancingModal(null), @@ -65,17 +68,15 @@ const ModelList: FC = ({ - {/* { - isConfigurable && canSystemConfig && ( - - {}} /> - - ) - } */} { isConfigurable && isCurrentWorkspaceManager && (
- onConfig()} /> +
) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx index 1a3039659a..efe2a8f94d 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx @@ -11,7 +11,7 @@ import classNames from '@/utils/classnames' import Tooltip from '@/app/components/base/tooltip' import Switch from '@/app/components/base/switch' import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' -import { Edit02, Plus02 } from '@/app/components/base/icons/src/vender/line/general' +import { Edit02 } from '@/app/components/base/icons/src/vender/line/general' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { useModalContextSelector } from '@/context/modal-context' import UpgradeBtn from '@/app/components/billing/upgrade-btn' @@ -19,6 +19,7 @@ import s from '@/app/components/custom/style.module.css' import GridMask from '@/app/components/base/grid-mask' import { useProviderContextSelector } from '@/context/provider-context' import { IS_CE_EDITION } from '@/config' +import { AddCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth' export type ModelLoadBalancingConfigsProps = { draftConfig?: ModelLoadBalancingConfig @@ -234,15 +235,10 @@ const ModelLoadBalancingConfigs = ({
) })} - -
toggleEntryModal()} - > -
- {t('common.modelProvider.addConfig')} -
-
+ toggleEntryModal()} + />
)} { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx index 9fb07401f7..a31d8a5c00 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx @@ -1,7 +1,13 @@ import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' -import type { ModelItem, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations' +import type { + Credential, + ModelItem, + ModelLoadBalancingConfig, + ModelLoadBalancingConfigEntry, + ModelProvider, +} from '../declarations' import { FormTypeEnum } from '../declarations' import ModelIcon from '../model-icon' import ModelName from '../model-name' @@ -13,17 +19,26 @@ import Button from '@/app/components/base/button' import { fetchModelLoadBalancingConfig } from '@/service/common' import Loading from '@/app/components/base/loading' import { useToastContext } from '@/app/components/base/toast' +import { SwitchCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth' export type ModelLoadBalancingModalProps = { provider: ModelProvider model: ModelItem + credential?: Credential open?: boolean onClose?: () => void onSave?: (provider: string) => void } // model balancing config modal -const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSave }: ModelLoadBalancingModalProps) => { +const ModelLoadBalancingModal = ({ + provider, + model, + credential, + open = false, + onClose, + onSave, +}: ModelLoadBalancingModalProps) => { const { t } = useTranslation() const { notify } = useToastContext() @@ -152,6 +167,11 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
{t('common.modelProvider.providerManaged')}
{t('common.modelProvider.providerManagedDescription')}
+ diff --git a/web/app/components/header/account-setting/model-provider-page/utils.ts b/web/app/components/header/account-setting/model-provider-page/utils.ts index 91ed6a96ff..f577a536dc 100644 --- a/web/app/components/header/account-setting/model-provider-page/utils.ts +++ b/web/app/components/header/account-setting/model-provider-page/utils.ts @@ -1,6 +1,5 @@ import { ValidatedStatus } from '../key-validator/declarations' import type { - CredentialFormSchemaRadio, CredentialFormSchemaTextInput, FormValue, ModelLoadBalancingConfig, @@ -181,7 +180,7 @@ export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => { show_on: [], } }), - } as CredentialFormSchemaRadio + } as any } export const genModelNameFormSchema = (model?: Pick) => { @@ -198,5 +197,5 @@ export const genModelNameFormSchema = (model?: Pick { }) } -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) => { +export const useGetProviderCredential = (enabled: boolean, provider: 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}`), + enabled, + queryKey: [NAME_SPACE, 'model-list', provider, credentialId], + queryFn: () => get<{ data: ProviderCredential }>(`/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`), }) } -export const useDeleteModelCredential = (providerName: string) => { +export const useAddProviderCredential = (provider: string) => { return useMutation({ - mutationFn: (credentialId: string) => del<{ result: string }>(`/workspaces/current/model-providers/${providerName}/credentials`, { - body: { - credential_id: credentialId, - }, + mutationFn: (data: ProviderCredential) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/credentials`, { + body: data, }), }) } -export const useSetModelCredentialDefault = (providerName: string) => { +export const useEditProviderCredential = (provider: string) => { return useMutation({ - mutationFn: (credentialId: string) => post<{ result: string }>(`/workspaces/current/model-providers/${providerName}/credentials/switch`, { - body: { - credential_id: credentialId, - }, + mutationFn: (data: ProviderCredential) => put<{ result: string }>(`/workspaces/current/model-providers/${provider}/credentials`, { + body: data, + }), + }) +} + +export const useDeleteProviderCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: { + credential_id: string + }) => del<{ result: string }>(`/workspaces/current/model-providers/${provider}/credentials`, { + body: data, + }), + }) +} + +export const useActiveProviderCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: { + credential_id: string + model?: string + model_type?: ModelTypeEnum + }) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/credentials/switch`, { + body: data, + }), + }) +} + +export const useGetModelCredential = ( + enabled: boolean, + provider: string, + credentialId?: string, + model?: string, + modelType?: string, + configFrom?: string, +) => { + return useQuery({ + enabled, + queryKey: [NAME_SPACE, 'model-list', provider, model, modelType, credentialId], + queryFn: () => get<{ data: ModelCredential }>(`/workspaces/current/model-providers/${provider}/models/credentials?model=${model}&model_type=${modelType}$credential_id=${credentialId}$config_from=${configFrom}`), + }) +} + +export const useAddModelCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: ModelCredential) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials`, { + body: data, + }), + }) +} + +export const useEditModelCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: ModelCredential) => put<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials`, { + body: data, + }), + }) +} + +export const useDeleteModelCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: { + credential_id: string + model?: string + model_type?: ModelTypeEnum + }) => del<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials`, { + body: data, + }), + }) +} + +export const useDeleteModel = (provider: string) => { + return useMutation({ + mutationFn: (data: { + model: string + model_type: ModelTypeEnum + }) => del<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials`, { + body: data, + }), + }) +} + +export const useActiveModelCredential = (provider: string) => { + return useMutation({ + mutationFn: (data: { + credential_id: string + model?: string + model_type?: ModelTypeEnum + }) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/models/credentials/switch`, { + body: data, }), }) }