mirror of
https://github.com/langgenius/dify.git
synced 2025-12-21 15:02:26 +00:00
Compare commits
19 Commits
fix/conver
...
feat/model
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6aa5273c5e | ||
|
|
473b465efb | ||
|
|
2e28a64d38 | ||
|
|
3f57e4a643 | ||
|
|
4e02abf784 | ||
|
|
61be6f5d2c | ||
|
|
e69797d738 | ||
|
|
415178fb0d | ||
|
|
1a642084b5 | ||
|
|
d9ccd74f0b | ||
|
|
4e6cb26778 | ||
|
|
12083de2ab | ||
|
|
3522eb51b6 | ||
|
|
8e1ea671bd | ||
|
|
d2eda60e0e | ||
|
|
b73487dd67 | ||
|
|
7ad64bfb60 | ||
|
|
1aec17f912 | ||
|
|
c1c7a43191 |
@@ -30,7 +30,7 @@ const BaseField = ({
|
|||||||
inputClassName,
|
inputClassName,
|
||||||
formSchema,
|
formSchema,
|
||||||
field,
|
field,
|
||||||
disabled,
|
disabled: propsDisabled,
|
||||||
}: BaseFieldProps) => {
|
}: BaseFieldProps) => {
|
||||||
const renderI18nObject = useRenderI18nObject()
|
const renderI18nObject = useRenderI18nObject()
|
||||||
const {
|
const {
|
||||||
@@ -40,7 +40,9 @@ const BaseField = ({
|
|||||||
options,
|
options,
|
||||||
labelClassName: formLabelClassName,
|
labelClassName: formLabelClassName,
|
||||||
show_on = [],
|
show_on = [],
|
||||||
|
disabled: formSchemaDisabled,
|
||||||
} = formSchema
|
} = formSchema
|
||||||
|
const disabled = propsDisabled || formSchemaDisabled
|
||||||
|
|
||||||
const memorizedLabel = useMemo(() => {
|
const memorizedLabel = useMemo(() => {
|
||||||
if (isValidElement(label))
|
if (isValidElement(label))
|
||||||
@@ -85,7 +87,7 @@ const BaseField = ({
|
|||||||
value: option.value,
|
value: option.value,
|
||||||
}
|
}
|
||||||
}) || []
|
}) || []
|
||||||
}, [options, renderI18nObject])
|
}, [options, renderI18nObject, optionValues])
|
||||||
const value = useStore(field.form.store, s => s.values[field.name])
|
const value = useStore(field.form.store, s => s.values[field.name])
|
||||||
const values = useStore(field.form.store, (s) => {
|
const values = useStore(field.form.store, (s) => {
|
||||||
return show_on.reduce((acc, condition) => {
|
return show_on.reduce((acc, condition) => {
|
||||||
@@ -182,9 +184,10 @@ const BaseField = ({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'system-sm-regular hover:bg-components-option-card-option-hover-bg hover:border-components-option-card-option-hover-border flex h-8 flex-[1] grow cursor-pointer items-center justify-center rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary',
|
'system-sm-regular hover:bg-components-option-card-option-hover-bg hover:border-components-option-card-option-hover-border flex h-8 flex-[1] grow cursor-pointer items-center justify-center rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary',
|
||||||
value === option.value && 'border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs',
|
value === option.value && 'border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs',
|
||||||
|
disabled && 'cursor-not-allowed opacity-50',
|
||||||
inputClassName,
|
inputClassName,
|
||||||
)}
|
)}
|
||||||
onClick={() => field.handleChange(option.value)}
|
onClick={() => !disabled && field.handleChange(option.value)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
formSchema.showRadioUI && (
|
formSchema.showRadioUI && (
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export type FormSchema = {
|
|||||||
labelClassName?: string
|
labelClassName?: string
|
||||||
validators?: AnyValidators
|
validators?: AnyValidators
|
||||||
showRadioUI?: boolean
|
showRadioUI?: boolean
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormValues = Record<string, any>
|
export type FormValues = Record<string, any>
|
||||||
|
|||||||
@@ -181,6 +181,27 @@ export type QuotaConfiguration = {
|
|||||||
is_valid: boolean
|
is_valid: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Credential = {
|
||||||
|
credential_id: string
|
||||||
|
credential_name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CustomModel = {
|
||||||
|
model: string
|
||||||
|
model_type: ModelTypeEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CustomModelCredential = CustomModel & {
|
||||||
|
credentials?: Record<string, any>
|
||||||
|
available_model_credentials?: Credential[]
|
||||||
|
current_credential_id?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CredentialWithModel = Credential & {
|
||||||
|
model: string
|
||||||
|
model_type: ModelTypeEnum
|
||||||
|
}
|
||||||
|
|
||||||
export type ModelProvider = {
|
export type ModelProvider = {
|
||||||
provider: string
|
provider: string
|
||||||
label: TypeWithI18N
|
label: TypeWithI18N
|
||||||
@@ -207,6 +228,10 @@ 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[]
|
||||||
|
custom_models?: CustomModelCredential[]
|
||||||
}
|
}
|
||||||
system_configuration: {
|
system_configuration: {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
@@ -272,9 +297,22 @@ export type ModelLoadBalancingConfigEntry = {
|
|||||||
in_cooldown?: boolean
|
in_cooldown?: boolean
|
||||||
/** cooldown time (in seconds) */
|
/** cooldown time (in seconds) */
|
||||||
ttl?: number
|
ttl?: number
|
||||||
|
credential_id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelLoadBalancingConfig = {
|
export type ModelLoadBalancingConfig = {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
configs: ModelLoadBalancingConfigEntry[]
|
configs: ModelLoadBalancingConfigEntry[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProviderCredential = {
|
||||||
|
credentials: Record<string, any>
|
||||||
|
name: string
|
||||||
|
credential_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModelCredential = {
|
||||||
|
credentials: Record<string, any>
|
||||||
|
load_balancing: ModelLoadBalancingConfig
|
||||||
|
available_credentials: Credential[]
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ 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,
|
||||||
|
CustomModel,
|
||||||
DefaultModel,
|
DefaultModel,
|
||||||
DefaultModelResponse,
|
DefaultModelResponse,
|
||||||
Model,
|
Model,
|
||||||
@@ -77,16 +79,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, isLoading: isPredefinedLoading } = 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, isLoading: isCustomizedLoading } = 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 +105,7 @@ export const useProviderCredentialsAndLoadBalancing = (
|
|||||||
: undefined
|
: undefined
|
||||||
}, [
|
}, [
|
||||||
configurationMethod,
|
configurationMethod,
|
||||||
|
credentialId,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
customFormSchemasValue?.credentials,
|
customFormSchemasValue?.credentials,
|
||||||
predefinedFormSchemasValue?.credentials,
|
predefinedFormSchemasValue?.credentials,
|
||||||
@@ -119,6 +123,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])
|
||||||
}
|
}
|
||||||
@@ -313,40 +318,57 @@ 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,
|
||||||
|
isModelCredential?: boolean,
|
||||||
|
credential?: Credential,
|
||||||
|
model?: CustomModel,
|
||||||
) => {
|
) => {
|
||||||
setShowModelModal({
|
setShowModelModal({
|
||||||
payload: {
|
payload: {
|
||||||
currentProvider: provider,
|
currentProvider: provider,
|
||||||
currentConfigurationMethod: configurationMethod,
|
currentConfigurationMethod: configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields: CustomConfigurationModelFixedFields,
|
||||||
|
isModelCredential,
|
||||||
|
credential,
|
||||||
|
model,
|
||||||
},
|
},
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
import SystemModelSelector from './system-model-selector'
|
import SystemModelSelector from './system-model-selector'
|
||||||
import ProviderAddedCard from './provider-added-card'
|
import ProviderAddedCard from './provider-added-card'
|
||||||
import type {
|
import type {
|
||||||
ConfigurationMethodEnum,
|
|
||||||
CustomConfigurationModelFixedFields,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from './declarations'
|
} from './declarations'
|
||||||
import {
|
import {
|
||||||
@@ -18,7 +16,6 @@ import {
|
|||||||
} from './declarations'
|
} from './declarations'
|
||||||
import {
|
import {
|
||||||
useDefaultModel,
|
useDefaultModel,
|
||||||
useModelModalHandler,
|
|
||||||
} from './hooks'
|
} from './hooks'
|
||||||
import InstallFromMarketplace from './install-from-marketplace'
|
import InstallFromMarketplace from './install-from-marketplace'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
@@ -84,8 +81,6 @@ const ModelProviderPage = ({ searchText }: Props) => {
|
|||||||
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
|
return [filteredConfiguredProviders, filteredNotConfiguredProviders]
|
||||||
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
|
}, [configuredProviders, debouncedSearchText, notConfiguredProviders])
|
||||||
|
|
||||||
const handleOpenModal = useModelModalHandler()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative -mt-2 pt-1'>
|
<div className='relative -mt-2 pt-1'>
|
||||||
<div className={cn('mb-2 flex items-center')}>
|
<div className={cn('mb-2 flex items-center')}>
|
||||||
@@ -126,7 +121,6 @@ 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)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +134,6 @@ 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)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
} from 'react'
|
||||||
|
import { RiAddLine } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Authorized } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomModelCredential,
|
||||||
|
ModelCredential,
|
||||||
|
ModelProvider,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
|
||||||
|
type AddCredentialInLoadBalancingProps = {
|
||||||
|
provider: ModelProvider
|
||||||
|
model: CustomModelCredential
|
||||||
|
configurationMethod: ConfigurationMethodEnum
|
||||||
|
modelCredential: ModelCredential
|
||||||
|
onSelectCredential: (credential: Credential) => void
|
||||||
|
onUpdate?: () => void
|
||||||
|
}
|
||||||
|
const AddCredentialInLoadBalancing = ({
|
||||||
|
provider,
|
||||||
|
model,
|
||||||
|
configurationMethod,
|
||||||
|
modelCredential,
|
||||||
|
onSelectCredential,
|
||||||
|
onUpdate,
|
||||||
|
}: AddCredentialInLoadBalancingProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
available_credentials,
|
||||||
|
} = modelCredential
|
||||||
|
const customModel = configurationMethod === ConfigurationMethodEnum.customizableModel
|
||||||
|
const renderTrigger = useCallback((open?: boolean) => {
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
'system-sm-medium flex h-8 items-center rounded-lg px-3 text-text-accent hover:bg-state-base-hover',
|
||||||
|
open && 'bg-state-base-hover',
|
||||||
|
)}>
|
||||||
|
<RiAddLine className='mr-2 h-4 w-4' />
|
||||||
|
{
|
||||||
|
customModel
|
||||||
|
? t('common.modelProvider.auth.addCredential')
|
||||||
|
: t('common.modelProvider.auth.addApiKey')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
renderTrigger={renderTrigger}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: customModel ? t('common.modelProvider.auth.modelCredentials') : t('common.modelProvider.auth.apiKeys'),
|
||||||
|
model,
|
||||||
|
credentials: available_credentials ?? [],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
configurationMethod={configurationMethod}
|
||||||
|
onItemClick={onSelectCredential}
|
||||||
|
placement='bottom-start'
|
||||||
|
onUpdate={onUpdate}
|
||||||
|
isModelCredential={customModel}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(AddCredentialInLoadBalancing)
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
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,
|
||||||
|
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,
|
||||||
|
useCustomModels,
|
||||||
|
} from './hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type AddCustomModelProps = {
|
||||||
|
provider: ModelProvider,
|
||||||
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
}
|
||||||
|
const AddCustomModel = ({
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
}: AddCustomModelProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const customModels = useCustomModels(provider)
|
||||||
|
const noModels = !customModels.length
|
||||||
|
const {
|
||||||
|
handleOpenModal,
|
||||||
|
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields, true)
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
handleOpenModal()
|
||||||
|
}, [handleOpenModal])
|
||||||
|
const ButtonComponent = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant='ghost-accent'
|
||||||
|
size='small'
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{t('common.modelProvider.addModel')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}, [handleClick])
|
||||||
|
|
||||||
|
const renderTrigger = useCallback((open?: boolean) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
size='small'
|
||||||
|
className={cn(open && 'bg-components-button-ghost-bg-hover')}
|
||||||
|
>
|
||||||
|
<RiAddCircleFill className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{t('common.modelProvider.addModel')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}, [t])
|
||||||
|
|
||||||
|
if (noModels)
|
||||||
|
return ButtonComponent
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
items={customModels.map(model => ({
|
||||||
|
model,
|
||||||
|
credentials: model.available_model_credentials ?? [],
|
||||||
|
}))}
|
||||||
|
renderTrigger={renderTrigger}
|
||||||
|
isModelCredential
|
||||||
|
enableAddModelCredential
|
||||||
|
bottomAddModelCredentialText={t('common.modelProvider.auth.addNewModel')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(AddCustomModel)
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
} from 'react'
|
||||||
|
import { RiAddLine } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import CredentialItem from './credential-item'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomModel,
|
||||||
|
CustomModelCredential,
|
||||||
|
} from '../../declarations'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
|
type AuthorizedItemProps = {
|
||||||
|
model?: CustomModelCredential
|
||||||
|
title?: string
|
||||||
|
disabled?: boolean
|
||||||
|
onDelete?: (credential?: Credential, model?: CustomModel) => void
|
||||||
|
onEdit?: (credential?: Credential, model?: CustomModel) => void
|
||||||
|
showItemSelectedIcon?: boolean
|
||||||
|
selectedCredentialId?: string
|
||||||
|
credentials: Credential[]
|
||||||
|
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
||||||
|
enableAddModelCredential?: boolean
|
||||||
|
}
|
||||||
|
export const AuthorizedItem = ({
|
||||||
|
model,
|
||||||
|
title,
|
||||||
|
credentials,
|
||||||
|
disabled,
|
||||||
|
onDelete,
|
||||||
|
onEdit,
|
||||||
|
showItemSelectedIcon,
|
||||||
|
selectedCredentialId,
|
||||||
|
onItemClick,
|
||||||
|
enableAddModelCredential,
|
||||||
|
}: AuthorizedItemProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const handleEdit = useCallback((credential?: Credential) => {
|
||||||
|
onEdit?.(credential, model)
|
||||||
|
}, [onEdit, model])
|
||||||
|
const handleDelete = useCallback((credential?: Credential) => {
|
||||||
|
onDelete?.(credential, model)
|
||||||
|
}, [onDelete, model])
|
||||||
|
const handleItemClick = useCallback((credential: Credential) => {
|
||||||
|
onItemClick?.(credential, model)
|
||||||
|
}, [onItemClick, model])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='p-1'>
|
||||||
|
{
|
||||||
|
model && (
|
||||||
|
<div
|
||||||
|
className='flex h-9 items-center'
|
||||||
|
>
|
||||||
|
<div className='h-5 w-5 shrink-0'></div>
|
||||||
|
<div
|
||||||
|
className='system-md-medium mx-1 grow truncate text-text-primary'
|
||||||
|
title={model.model}
|
||||||
|
>
|
||||||
|
{title ?? model.model}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
enableAddModelCredential && (
|
||||||
|
<Tooltip
|
||||||
|
asChild
|
||||||
|
popupContent={t('common.modelProvider.auth.addModelCredential')}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className='h-6 w-6 shrink-0 rounded-full p-0'
|
||||||
|
size='small'
|
||||||
|
variant='secondary-accent'
|
||||||
|
onClick={() => handleEdit?.()}
|
||||||
|
>
|
||||||
|
<RiAddLine className='h-4 w-4' />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
credentials.map(credential => (
|
||||||
|
<CredentialItem
|
||||||
|
key={credential.credential_id}
|
||||||
|
credential={credential}
|
||||||
|
disabled={disabled}
|
||||||
|
onDelete={handleDelete}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
showSelectedIcon={showItemSelectedIcon}
|
||||||
|
selectedCredentialId={selectedCredentialId}
|
||||||
|
onItemClick={handleItemClick}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(AuthorizedItem)
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
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 CredentialItemProps = {
|
||||||
|
credential: Credential
|
||||||
|
disabled?: boolean
|
||||||
|
onDelete?: (credential: Credential) => void
|
||||||
|
onEdit?: (credential?: Credential) => void
|
||||||
|
onItemClick?: (credential: Credential) => void
|
||||||
|
disableRename?: boolean
|
||||||
|
disableEdit?: boolean
|
||||||
|
disableDelete?: boolean
|
||||||
|
showSelectedIcon?: boolean
|
||||||
|
selectedCredentialId?: string
|
||||||
|
}
|
||||||
|
const CredentialItem = ({
|
||||||
|
credential,
|
||||||
|
disabled,
|
||||||
|
onDelete,
|
||||||
|
onEdit,
|
||||||
|
onItemClick,
|
||||||
|
disableRename,
|
||||||
|
disableEdit,
|
||||||
|
disableDelete,
|
||||||
|
showSelectedIcon,
|
||||||
|
selectedCredentialId,
|
||||||
|
}: CredentialItemProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const showAction = useMemo(() => {
|
||||||
|
return !(disableRename && disableEdit && disableDelete)
|
||||||
|
}, [disableRename, disableEdit, disableDelete])
|
||||||
|
|
||||||
|
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)}
|
||||||
|
>
|
||||||
|
<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)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary hover:text-text-destructive' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(CredentialItem)
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import {
|
||||||
|
RiAddLine,
|
||||||
|
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 type {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
|
Credential,
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
|
CustomModel,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../../declarations'
|
||||||
|
import { useAuth } from '../hooks'
|
||||||
|
import AuthorizedItem from './authorized-item'
|
||||||
|
|
||||||
|
type AuthorizedProps = {
|
||||||
|
provider: ModelProvider,
|
||||||
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
isModelCredential?: boolean
|
||||||
|
items: {
|
||||||
|
model?: CustomModel
|
||||||
|
credentials: Credential[]
|
||||||
|
}[]
|
||||||
|
selectedCredential?: Credential
|
||||||
|
disabled?: boolean
|
||||||
|
renderTrigger?: (open?: boolean) => React.ReactNode
|
||||||
|
isOpen?: boolean
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
|
offset?: PortalToFollowElemOptions['offset']
|
||||||
|
placement?: PortalToFollowElemOptions['placement']
|
||||||
|
triggerPopupSameWidth?: boolean
|
||||||
|
popupClassName?: string
|
||||||
|
showItemSelectedIcon?: boolean
|
||||||
|
onUpdate?: () => void
|
||||||
|
onItemClick?: (credential: Credential, model?: CustomModel) => void
|
||||||
|
enableAddModelCredential?: boolean
|
||||||
|
bottomAddModelCredentialText?: string
|
||||||
|
}
|
||||||
|
const Authorized = ({
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
items,
|
||||||
|
isModelCredential,
|
||||||
|
selectedCredential,
|
||||||
|
disabled,
|
||||||
|
renderTrigger,
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
offset = 8,
|
||||||
|
placement = 'bottom-end',
|
||||||
|
triggerPopupSameWidth = false,
|
||||||
|
popupClassName,
|
||||||
|
showItemSelectedIcon,
|
||||||
|
onUpdate,
|
||||||
|
onItemClick,
|
||||||
|
enableAddModelCredential,
|
||||||
|
bottomAddModelCredentialText,
|
||||||
|
}: AuthorizedProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [isLocalOpen, setIsLocalOpen] = useState(false)
|
||||||
|
const mergedIsOpen = isOpen ?? isLocalOpen
|
||||||
|
const setMergedIsOpen = useCallback((open: boolean) => {
|
||||||
|
if (onOpenChange)
|
||||||
|
onOpenChange(open)
|
||||||
|
|
||||||
|
setIsLocalOpen(open)
|
||||||
|
}, [onOpenChange])
|
||||||
|
const {
|
||||||
|
openConfirmDelete,
|
||||||
|
closeConfirmDelete,
|
||||||
|
doingAction,
|
||||||
|
handleActiveCredential,
|
||||||
|
handleConfirmDelete,
|
||||||
|
deleteCredentialId,
|
||||||
|
handleOpenModal,
|
||||||
|
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onUpdate)
|
||||||
|
|
||||||
|
const handleEdit = useCallback((credential?: Credential, model?: CustomModel) => {
|
||||||
|
handleOpenModal(credential, model)
|
||||||
|
setMergedIsOpen(false)
|
||||||
|
}, [handleOpenModal, setMergedIsOpen])
|
||||||
|
|
||||||
|
const handleItemClick = useCallback((credential: Credential, model?: CustomModel) => {
|
||||||
|
if (!onItemClick)
|
||||||
|
return handleActiveCredential(credential, model)
|
||||||
|
|
||||||
|
onItemClick?.(credential, model)
|
||||||
|
}, [handleActiveCredential, onItemClick])
|
||||||
|
|
||||||
|
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(
|
||||||
|
'w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
|
||||||
|
popupClassName,
|
||||||
|
)}>
|
||||||
|
<div className='max-h-[304px] overflow-y-auto'>
|
||||||
|
{
|
||||||
|
items.map((item, index) => (
|
||||||
|
<AuthorizedItem
|
||||||
|
key={index}
|
||||||
|
model={item.model}
|
||||||
|
credentials={item.credentials}
|
||||||
|
disabled={disabled}
|
||||||
|
onDelete={openConfirmDelete}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
showItemSelectedIcon={showItemSelectedIcon}
|
||||||
|
selectedCredentialId={selectedCredential?.credential_id}
|
||||||
|
onItemClick={handleItemClick}
|
||||||
|
enableAddModelCredential={enableAddModelCredential}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
|
{
|
||||||
|
isModelCredential && (
|
||||||
|
<div
|
||||||
|
onClick={() => handleEdit()}
|
||||||
|
className='system-xs-medium flex h-[30px] cursor-pointer items-center px-3 text-text-accent-light-mode-only'
|
||||||
|
>
|
||||||
|
<RiAddLine className='mr-1 h-4 w-4' />
|
||||||
|
{bottomAddModelCredentialText ?? t('common.modelProvider.auth.addModelCredential')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!isModelCredential && (
|
||||||
|
<div className='p-2'>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleEdit()}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
{t('common.modelProvider.auth.addApiKey')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
{
|
||||||
|
deleteCredentialId && (
|
||||||
|
<Confirm
|
||||||
|
isShow
|
||||||
|
title={t('common.modelProvider.confirmDelete')}
|
||||||
|
isDisabled={doingAction}
|
||||||
|
onCancel={closeConfirmDelete}
|
||||||
|
onConfirm={handleConfirmDelete}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Authorized)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type ConfigModelProps = {
|
||||||
|
className?: string
|
||||||
|
onClick?: () => void
|
||||||
|
}
|
||||||
|
const ConfigModel = ({
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}: ConfigModelProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant='secondary'
|
||||||
|
size='small'
|
||||||
|
className={cn(
|
||||||
|
'shrink-0',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='mr-1 h-4 w-4' />
|
||||||
|
{t('common.operation.config')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConfigModel)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiEqualizer2Line,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
} from '@/app/components/base/button'
|
||||||
|
import type {
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
|
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, useCredentialStatus } from './hooks'
|
||||||
|
|
||||||
|
type ConfigProviderProps = {
|
||||||
|
provider: ModelProvider,
|
||||||
|
configurationMethod: ConfigurationMethodEnum,
|
||||||
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||||
|
}
|
||||||
|
const ConfigProvider = ({
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
}: ConfigProviderProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
handleOpenModal,
|
||||||
|
} = useAuth(provider, configurationMethod, currentCustomConfigurationModelFixedFields)
|
||||||
|
const {
|
||||||
|
hasCredential,
|
||||||
|
authorized,
|
||||||
|
current_credential_id,
|
||||||
|
current_credential_name,
|
||||||
|
available_credentials,
|
||||||
|
} = useCredentialStatus(provider)
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (!hasCredential)
|
||||||
|
handleOpenModal()
|
||||||
|
}, [handleOpenModal, hasCredential])
|
||||||
|
|
||||||
|
const ButtonComponent = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
className='grow'
|
||||||
|
size='small'
|
||||||
|
onClick={handleClick}
|
||||||
|
variant={!authorized ? 'secondary-accent' : 'secondary'}
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
||||||
|
{t('common.operation.setup')}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}, [handleClick, authorized])
|
||||||
|
|
||||||
|
if (!hasCredential)
|
||||||
|
return ButtonComponent
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.predefinedModel}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
model: {
|
||||||
|
model: t('common.modelProvider.auth.apiKeys'),
|
||||||
|
} as any,
|
||||||
|
credentials: available_credentials ?? [],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
selectedCredential={{
|
||||||
|
credential_id: current_credential_id ?? '',
|
||||||
|
credential_name: current_credential_name ?? '',
|
||||||
|
}}
|
||||||
|
showItemSelectedIcon
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ConfigProvider)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
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'
|
||||||
|
export * from './use-credential-data'
|
||||||
@@ -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, isModelCredential?: boolean, credentialId?: string, model?: CustomModel, configFrom?: string) => {
|
||||||
|
const providerData = useGetProviderCredential(!isModelCredential && !!credentialId, provider, credentialId)
|
||||||
|
const modelData = useGetModelCredential(!!isModelCredential && !!credentialId, provider, credentialId, model?.model, model?.model_type, configFrom)
|
||||||
|
return isModelCredential ? 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
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,
|
||||||
|
isModelCredential?: boolean,
|
||||||
|
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<string | null>(null)
|
||||||
|
const pendingOperationModel = useRef<CustomModel | null>(null)
|
||||||
|
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||||
|
const openConfirmDelete = useCallback((credential?: Credential, model?: CustomModel) => {
|
||||||
|
if (credential)
|
||||||
|
pendingOperationCredentialId.current = credential.credential_id
|
||||||
|
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 (credential: Credential, model?: CustomModel) => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await getActiveCredentialService(!!model)({
|
||||||
|
credential_id: credential.credential_id,
|
||||||
|
model: model?.model,
|
||||||
|
model_type: model?.model_type,
|
||||||
|
})
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
handleRefreshModel(provider, configurationMethod, undefined)
|
||||||
|
}
|
||||||
|
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(!!isModelCredential)({
|
||||||
|
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
|
||||||
|
pendingOperationModel.current = null
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [onUpdate, notify, t, handleSetDoingAction, getDeleteCredentialService, isModelCredential])
|
||||||
|
const handleAddCredential = useCallback((model?: CustomModel) => {
|
||||||
|
if (model)
|
||||||
|
pendingOperationModel.current = model
|
||||||
|
}, [])
|
||||||
|
const handleSaveCredential = useCallback(async (payload: Record<string, any>) => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
|
||||||
|
let res: { result?: string } = {}
|
||||||
|
if (payload.credential_id)
|
||||||
|
res = await getEditCredentialService(!!isModelCredential)(payload as any)
|
||||||
|
else
|
||||||
|
res = await getAddCredentialService(!!isModelCredential)(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((credential?: Credential, model?: CustomModel) => {
|
||||||
|
handleOpenModelModal(
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
isModelCredential,
|
||||||
|
credential,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
}, [handleOpenModelModal, provider, configurationMethod, currentCustomConfigurationModelFixedFields, isModelCredential])
|
||||||
|
|
||||||
|
return {
|
||||||
|
pendingOperationCredentialId,
|
||||||
|
pendingOperationModel,
|
||||||
|
openConfirmDelete,
|
||||||
|
closeConfirmDelete,
|
||||||
|
doingAction,
|
||||||
|
handleActiveCredential,
|
||||||
|
handleConfirmDelete,
|
||||||
|
handleAddCredential,
|
||||||
|
deleteCredentialId,
|
||||||
|
handleSaveCredential,
|
||||||
|
handleOpenModal,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useGetCredential } from './use-auth-service'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomModelCredential,
|
||||||
|
ModelProvider,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
|
||||||
|
export const useCredentialData = (provider: ModelProvider, providerFormSchemaPredefined: boolean, isModelCredential?: boolean, credential?: Credential, model?: CustomModelCredential) => {
|
||||||
|
const configFrom = useMemo(() => {
|
||||||
|
if (providerFormSchemaPredefined)
|
||||||
|
return 'predefined-model'
|
||||||
|
return 'custom-model'
|
||||||
|
}, [providerFormSchemaPredefined])
|
||||||
|
const {
|
||||||
|
isLoading,
|
||||||
|
data: credentialData = {},
|
||||||
|
} = useGetCredential(provider.provider, isModelCredential, credential?.credential_id, model, configFrom)
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoading,
|
||||||
|
credentialData,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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])
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type {
|
||||||
|
ModelProvider,
|
||||||
|
} from '../../declarations'
|
||||||
|
|
||||||
|
export const useCustomModels = (provider: ModelProvider) => {
|
||||||
|
const { custom_models } = provider.custom_configuration
|
||||||
|
|
||||||
|
return custom_models || []
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomModelCredential,
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../../declarations'
|
||||||
|
import {
|
||||||
|
genModelNameFormSchema,
|
||||||
|
genModelTypeFormSchema,
|
||||||
|
} from '../../utils'
|
||||||
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
|
|
||||||
|
export const useModelFormSchemas = (
|
||||||
|
provider: ModelProvider,
|
||||||
|
providerFormSchemaPredefined: boolean,
|
||||||
|
credentials?: Record<string, any>,
|
||||||
|
credential?: Credential,
|
||||||
|
model?: CustomModelCredential,
|
||||||
|
draftConfig?: ModelLoadBalancingConfig,
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
provider_credential_schema,
|
||||||
|
supported_model_types,
|
||||||
|
model_credential_schema,
|
||||||
|
} = provider
|
||||||
|
const formSchemas = useMemo(() => {
|
||||||
|
const modelTypeSchema = genModelTypeFormSchema(supported_model_types)
|
||||||
|
const modelNameSchema = genModelNameFormSchema(model_credential_schema?.model)
|
||||||
|
if (!!model) {
|
||||||
|
modelTypeSchema.disabled = true
|
||||||
|
modelNameSchema.disabled = true
|
||||||
|
}
|
||||||
|
return providerFormSchemaPredefined
|
||||||
|
? provider_credential_schema.credential_form_schemas
|
||||||
|
: [
|
||||||
|
modelTypeSchema,
|
||||||
|
modelNameSchema,
|
||||||
|
...(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,
|
||||||
|
model,
|
||||||
|
])
|
||||||
|
|
||||||
|
const formSchemasWithAuthorizationName = useMemo(() => {
|
||||||
|
const authorizationNameSchema = {
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
variable: '__authorization_name__',
|
||||||
|
label: t('plugin.auth.authorizationName'),
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
authorizationNameSchema,
|
||||||
|
...formSchemas,
|
||||||
|
]
|
||||||
|
}, [formSchemas, t])
|
||||||
|
|
||||||
|
const formValues = useMemo(() => {
|
||||||
|
let result = {}
|
||||||
|
if (credentials)
|
||||||
|
result = { ...credentials }
|
||||||
|
if (credential)
|
||||||
|
result = { ...result, __authorization_name__: credential?.credential_name }
|
||||||
|
if (model)
|
||||||
|
result = { ...result, __model_name: model?.model, __model_type: model?.model_type }
|
||||||
|
return result
|
||||||
|
}, [credentials, credential, model])
|
||||||
|
|
||||||
|
return {
|
||||||
|
formSchemas: formSchemasWithAuthorizationName,
|
||||||
|
formValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
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'
|
||||||
|
export { default as ConfigModel } from './config-model'
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
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 Indicator from '@/app/components/header/indicator'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import Authorized from './authorized'
|
||||||
|
import type {
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
|
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
|
import { useCredentialStatus } from './hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type SwitchCredentialInLoadBalancingProps = {
|
||||||
|
provider: ModelProvider
|
||||||
|
draftConfig?: ModelLoadBalancingConfig
|
||||||
|
setDraftConfig: Dispatch<SetStateAction<ModelLoadBalancingConfig | undefined>>
|
||||||
|
}
|
||||||
|
const SwitchCredentialInLoadBalancing = ({
|
||||||
|
provider,
|
||||||
|
draftConfig,
|
||||||
|
}: SwitchCredentialInLoadBalancingProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const {
|
||||||
|
available_credentials,
|
||||||
|
current_credential_name,
|
||||||
|
} = useCredentialStatus(provider)
|
||||||
|
|
||||||
|
const handleItemClick = useCallback(() => {
|
||||||
|
console.log('handleItemClick', draftConfig)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Button
|
||||||
|
variant='secondary'
|
||||||
|
className={cn(
|
||||||
|
'shrink-0 space-x-1',
|
||||||
|
authRemoved && 'text-components-button-destructive-secondary-text',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Indicator
|
||||||
|
className='mr-2'
|
||||||
|
color={authRemoved ? 'red' : 'green'}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
authRemoved ? t('common.model.authRemoved') : name
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!authRemoved && (
|
||||||
|
<Badge>enterprise</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<RiArrowDownSLine className='h-4 w-4' />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}, [current_credential_name, t, draftConfig, available_credentials])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
model: {
|
||||||
|
model: t('common.modelProvider.modelCredentials'),
|
||||||
|
} as any,
|
||||||
|
credentials: available_credentials || [],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
renderTrigger={renderTrigger}
|
||||||
|
onItemClick={handleItemClick}
|
||||||
|
isModelCredential
|
||||||
|
enableAddModelCredential
|
||||||
|
bottomAddModelCredentialText={t('common.modelProvider.addModelCredential')}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(SwitchCredentialInLoadBalancing)
|
||||||
@@ -2,43 +2,20 @@ import type { FC } from 'react'
|
|||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useRef,
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
|
||||||
RiErrorWarningFill,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import type {
|
import type {
|
||||||
CredentialFormSchema,
|
|
||||||
CredentialFormSchemaRadio,
|
|
||||||
CredentialFormSchemaSelect,
|
|
||||||
CustomConfigurationModelFixedFields,
|
CustomConfigurationModelFixedFields,
|
||||||
FormValue,
|
|
||||||
ModelLoadBalancingConfig,
|
|
||||||
ModelLoadBalancingConfigEntry,
|
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
CustomConfigurationStatusEnum,
|
|
||||||
FormTypeEnum,
|
FormTypeEnum,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
import {
|
|
||||||
genModelNameFormSchema,
|
|
||||||
genModelTypeFormSchema,
|
|
||||||
removeCredentials,
|
|
||||||
saveCredentials,
|
|
||||||
} from '../utils'
|
|
||||||
import {
|
import {
|
||||||
useLanguage,
|
useLanguage,
|
||||||
useProviderCredentialsAndLoadBalancing,
|
|
||||||
} from '../hooks'
|
} from '../hooks'
|
||||||
import { useValidate } from '../../key-validator/hooks'
|
|
||||||
import { ValidatedStatus } from '../../key-validator/declarations'
|
|
||||||
import ModelLoadBalancingConfigs from '../provider-added-card/model-load-balancing-configs'
|
|
||||||
import Form from './Form'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
||||||
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
|
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
@@ -46,9 +23,23 @@ import {
|
|||||||
PortalToFollowElem,
|
PortalToFollowElem,
|
||||||
PortalToFollowElemContent,
|
PortalToFollowElemContent,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
|
import AuthForm from '@/app/components/base/form/form-scenarios/auth'
|
||||||
|
import type {
|
||||||
|
FormRefObject,
|
||||||
|
FormSchema,
|
||||||
|
} from '@/app/components/base/form/types'
|
||||||
|
import { useModelFormSchemas } from '../model-auth/hooks'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomModel,
|
||||||
|
} from '../declarations'
|
||||||
|
import Loading from '@/app/components/base/loading'
|
||||||
|
import {
|
||||||
|
useAuth,
|
||||||
|
useCredentialData,
|
||||||
|
} from '@/app/components/header/account-setting/model-provider-page/model-auth/hooks'
|
||||||
|
|
||||||
type ModelModalProps = {
|
type ModelModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
@@ -56,6 +47,9 @@ type ModelModalProps = {
|
|||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
onSave: () => void
|
onSave: () => void
|
||||||
|
model?: CustomModel
|
||||||
|
credential?: Credential
|
||||||
|
isModelCredential?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModelModal: FC<ModelModalProps> = ({
|
const ModelModal: FC<ModelModalProps> = ({
|
||||||
@@ -64,210 +58,71 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSave,
|
onSave,
|
||||||
|
model,
|
||||||
|
credential,
|
||||||
|
isModelCredential,
|
||||||
}) => {
|
}) => {
|
||||||
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
||||||
|
const {
|
||||||
|
isLoading,
|
||||||
|
credentialData,
|
||||||
|
} = useCredentialData(provider, providerFormSchemaPredefined, isModelCredential, credential, model)
|
||||||
|
const {
|
||||||
|
handleSaveCredential,
|
||||||
|
handleConfirmDelete,
|
||||||
|
deleteCredentialId,
|
||||||
|
closeConfirmDelete,
|
||||||
|
openConfirmDelete,
|
||||||
|
doingAction,
|
||||||
|
} = useAuth(provider, configurateMethod, currentCustomConfigurationModelFixedFields, isModelCredential, onSave)
|
||||||
const {
|
const {
|
||||||
credentials: formSchemasValue,
|
credentials: formSchemasValue,
|
||||||
loadBalancing: originalConfig,
|
} = credentialData as any
|
||||||
mutate,
|
|
||||||
} = useProviderCredentialsAndLoadBalancing(
|
|
||||||
provider.provider,
|
|
||||||
configurateMethod,
|
|
||||||
providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active,
|
|
||||||
currentCustomConfigurationModelFixedFields,
|
|
||||||
)
|
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
const isEditMode = !!formSchemasValue && isCurrentWorkspaceManager
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const [loading, setLoading] = useState(false)
|
const {
|
||||||
const [showConfirm, setShowConfirm] = useState(false)
|
formSchemas,
|
||||||
|
formValues,
|
||||||
|
} = useModelFormSchemas(provider, providerFormSchemaPredefined, formSchemasValue, credential, model)
|
||||||
|
const formRef = useRef<FormRefObject>(null)
|
||||||
|
|
||||||
const [draftConfig, setDraftConfig] = useState<ModelLoadBalancingConfig>()
|
const handleSave = useCallback(async () => {
|
||||||
const originalConfigMap = useMemo(() => {
|
const {
|
||||||
if (!originalConfig)
|
isCheckValidated,
|
||||||
return {}
|
values,
|
||||||
return originalConfig?.configs.reduce((prev, config) => {
|
} = formRef.current?.getFormValues({
|
||||||
if (config.id)
|
needCheckValidatedValues: true,
|
||||||
prev[config.id] = config
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
return prev
|
}) || { isCheckValidated: false, values: {} }
|
||||||
}, {} as Record<string, ModelLoadBalancingConfigEntry>)
|
if (!isCheckValidated)
|
||||||
}, [originalConfig])
|
return
|
||||||
useEffect(() => {
|
|
||||||
if (originalConfig && !draftConfig)
|
|
||||||
setDraftConfig(originalConfig)
|
|
||||||
}, [draftConfig, originalConfig])
|
|
||||||
|
|
||||||
const formSchemas = useMemo(() => {
|
const {
|
||||||
return providerFormSchemaPredefined
|
__authorization_name__,
|
||||||
? provider.provider_credential_schema.credential_form_schemas
|
__model_name,
|
||||||
: [
|
__model_type,
|
||||||
genModelTypeFormSchema(provider.supported_model_types),
|
...rest
|
||||||
genModelNameFormSchema(provider.model_credential_schema?.model),
|
} = values
|
||||||
...(draftConfig?.enabled ? [] : provider.model_credential_schema.credential_form_schemas),
|
if (__model_name && __model_type) {
|
||||||
]
|
handleSaveCredential({
|
||||||
}, [
|
credential_id: credential?.credential_id,
|
||||||
providerFormSchemaPredefined,
|
credentials: rest,
|
||||||
provider.provider_credential_schema?.credential_form_schemas,
|
name: __authorization_name__,
|
||||||
provider.supported_model_types,
|
model: __model_name,
|
||||||
provider.model_credential_schema?.credential_form_schemas,
|
model_type: __model_type,
|
||||||
provider.model_credential_schema?.model,
|
})
|
||||||
draftConfig?.enabled,
|
|
||||||
])
|
|
||||||
const [
|
|
||||||
requiredFormSchemas,
|
|
||||||
defaultFormSchemaValue,
|
|
||||||
showOnVariableMap,
|
|
||||||
] = useMemo(() => {
|
|
||||||
const requiredFormSchemas: CredentialFormSchema[] = []
|
|
||||||
const defaultFormSchemaValue: Record<string, string | number> = {}
|
|
||||||
const showOnVariableMap: Record<string, string[]> = {}
|
|
||||||
|
|
||||||
formSchemas.forEach((formSchema) => {
|
|
||||||
if (formSchema.required)
|
|
||||||
requiredFormSchemas.push(formSchema)
|
|
||||||
|
|
||||||
if (formSchema.default)
|
|
||||||
defaultFormSchemaValue[formSchema.variable] = formSchema.default
|
|
||||||
|
|
||||||
if (formSchema.show_on.length) {
|
|
||||||
formSchema.show_on.forEach((showOnItem) => {
|
|
||||||
if (!showOnVariableMap[showOnItem.variable])
|
|
||||||
showOnVariableMap[showOnItem.variable] = []
|
|
||||||
|
|
||||||
if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable))
|
|
||||||
showOnVariableMap[showOnItem.variable].push(formSchema.variable)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formSchema.type === FormTypeEnum.select || formSchema.type === FormTypeEnum.radio) {
|
|
||||||
(formSchema as (CredentialFormSchemaRadio | CredentialFormSchemaSelect)).options.forEach((option) => {
|
|
||||||
if (option.show_on.length) {
|
|
||||||
option.show_on.forEach((showOnItem) => {
|
|
||||||
if (!showOnVariableMap[showOnItem.variable])
|
|
||||||
showOnVariableMap[showOnItem.variable] = []
|
|
||||||
|
|
||||||
if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable))
|
|
||||||
showOnVariableMap[showOnItem.variable].push(formSchema.variable)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [
|
|
||||||
requiredFormSchemas,
|
|
||||||
defaultFormSchemaValue,
|
|
||||||
showOnVariableMap,
|
|
||||||
]
|
|
||||||
}, [formSchemas])
|
|
||||||
const initialFormSchemasValue: Record<string, string | number> = useMemo(() => {
|
|
||||||
return {
|
|
||||||
...defaultFormSchemaValue,
|
|
||||||
...formSchemasValue,
|
|
||||||
} as unknown as Record<string, string | number>
|
|
||||||
}, [formSchemasValue, defaultFormSchemaValue])
|
|
||||||
const [value, setValue] = useState(initialFormSchemasValue)
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialFormSchemasValue)
|
|
||||||
}, [initialFormSchemasValue])
|
|
||||||
const [_, validating, validatedStatusState] = useValidate(value)
|
|
||||||
const filteredRequiredFormSchemas = requiredFormSchemas.filter((requiredFormSchema) => {
|
|
||||||
if (requiredFormSchema.show_on.length && requiredFormSchema.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
|
|
||||||
return true
|
|
||||||
|
|
||||||
if (!requiredFormSchema.show_on.length)
|
|
||||||
return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleValueChange = (v: FormValue) => {
|
|
||||||
setValue(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 encodeSecretValues = useCallback((v: FormValue) => {
|
|
||||||
const result = { ...v }
|
|
||||||
extendedSecretFormSchemas.forEach(({ variable }) => {
|
|
||||||
if (result[variable] === formSchemasValue?.[variable] && result[variable] !== undefined)
|
|
||||||
result[variable] = '[__HIDDEN__]'
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}, [extendedSecretFormSchemas, formSchemasValue])
|
|
||||||
|
|
||||||
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 res = await saveCredentials(
|
|
||||||
providerFormSchemaPredefined,
|
|
||||||
provider.provider,
|
|
||||||
encodeSecretValues(value),
|
|
||||||
{
|
|
||||||
...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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
else {
|
||||||
setLoading(false)
|
handleSaveCredential({
|
||||||
|
credential_id: credential?.credential_id,
|
||||||
|
credentials: rest,
|
||||||
|
name: __authorization_name__,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}, [handleSaveCredential, credential?.credential_id, model])
|
||||||
|
|
||||||
const handleRemove = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true)
|
|
||||||
|
|
||||||
const res = await removeCredentials(
|
|
||||||
providerFormSchemaPredefined,
|
|
||||||
provider.provider,
|
|
||||||
value,
|
|
||||||
)
|
|
||||||
if (res.result === 'success') {
|
|
||||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
|
||||||
mutate()
|
|
||||||
onSave()
|
|
||||||
onCancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderTitlePrefix = () => {
|
const renderTitlePrefix = () => {
|
||||||
const prefix = isEditMode ? t('common.operation.setup') : t('common.operation.add')
|
const prefix = isEditMode ? t('common.operation.setup') : t('common.operation.add')
|
||||||
@@ -285,23 +140,29 @@ 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'>
|
||||||
<Form
|
{
|
||||||
value={value}
|
isLoading && (
|
||||||
onChange={handleValueChange}
|
<div className='flex items-center justify-center'>
|
||||||
formSchemas={formSchemas}
|
<Loading />
|
||||||
validating={validating}
|
</div>
|
||||||
validatedSuccess={validatedStatusState.status === ValidatedStatus.Success}
|
)
|
||||||
showOnVariableMap={showOnVariableMap}
|
}
|
||||||
isEditMode={isEditMode}
|
{
|
||||||
/>
|
!isLoading && (
|
||||||
<div className='mb-4 mt-1 border-t-[0.5px] border-t-divider-regular' />
|
<AuthForm
|
||||||
<ModelLoadBalancingConfigs withSwitch {...{
|
formSchemas={formSchemas.map((formSchema) => {
|
||||||
draftConfig,
|
return {
|
||||||
setDraftConfig,
|
...formSchema,
|
||||||
provider,
|
name: formSchema.variable,
|
||||||
currentCustomConfigurationModelFixedFields,
|
showRadioUI: formSchema.type === FormTypeEnum.radio,
|
||||||
configurationMethod: configurateMethod,
|
}
|
||||||
}} />
|
}) as FormSchema[]}
|
||||||
|
defaultValues={formValues}
|
||||||
|
inputClassName='justify-start'
|
||||||
|
ref={formRef}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='sticky bottom-0 -mx-2 mt-2 flex flex-wrap items-center justify-between gap-y-2 bg-components-panel-bg px-2 pb-6 pt-4'>
|
<div className='sticky bottom-0 -mx-2 mt-2 flex flex-wrap items-center justify-between gap-y-2 bg-components-panel-bg px-2 pb-6 pt-4'>
|
||||||
@@ -327,7 +188,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
variant='warning'
|
variant='warning'
|
||||||
size='large'
|
size='large'
|
||||||
className='mr-2'
|
className='mr-2'
|
||||||
onClick={() => setShowConfirm(true)}
|
onClick={() => openConfirmDelete(credential, model)}
|
||||||
>
|
>
|
||||||
{t('common.operation.remove')}
|
{t('common.operation.remove')}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -344,12 +205,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
size='large'
|
size='large'
|
||||||
variant='primary'
|
variant='primary'
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={
|
disabled={isLoading || doingAction}
|
||||||
loading
|
|
||||||
|| filteredRequiredFormSchemas.some(item => value[item.variable] === undefined)
|
|
||||||
|| (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
>
|
>
|
||||||
{t('common.operation.save')}
|
{t('common.operation.save')}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -357,38 +213,28 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='border-t-[0.5px] border-t-divider-regular'>
|
<div className='border-t-[0.5px] border-t-divider-regular'>
|
||||||
{
|
<div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
||||||
(validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message)
|
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
||||||
? (
|
{t('common.modelProvider.encrypted.front')}
|
||||||
<div className='flex bg-background-section-burn px-[10px] py-3 text-xs text-[#D92D20]'>
|
<a
|
||||||
<RiErrorWarningFill className='mr-2 mt-[1px] h-[14px] w-[14px]' />
|
className='mx-1 text-text-accent'
|
||||||
{validatedStatusState.message}
|
target='_blank' rel='noopener noreferrer'
|
||||||
</div>
|
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
||||||
)
|
>
|
||||||
: (
|
PKCS1_OAEP
|
||||||
<div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
</a>
|
||||||
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
{t('common.modelProvider.encrypted.back')}
|
||||||
{t('common.modelProvider.encrypted.front')}
|
</div>
|
||||||
<a
|
|
||||||
className='mx-1 text-text-accent'
|
|
||||||
target='_blank' rel='noopener noreferrer'
|
|
||||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
|
||||||
>
|
|
||||||
PKCS1_OAEP
|
|
||||||
</a>
|
|
||||||
{t('common.modelProvider.encrypted.back')}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
showConfirm && (
|
deleteCredentialId && (
|
||||||
<Confirm
|
<Confirm
|
||||||
|
isShow
|
||||||
title={t('common.modelProvider.confirmDelete')}
|
title={t('common.modelProvider.confirmDelete')}
|
||||||
isShow={showConfirm}
|
isDisabled={doingAction}
|
||||||
onCancel={() => setShowConfirm(false)}
|
onCancel={closeConfirmDelete}
|
||||||
onConfirm={handleRemove}
|
onConfirm={handleConfirmDelete}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,348 +0,0 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import {
|
|
||||||
memo,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import {
|
|
||||||
RiErrorWarningFill,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import type {
|
|
||||||
CredentialFormSchema,
|
|
||||||
CredentialFormSchemaRadio,
|
|
||||||
CredentialFormSchemaSelect,
|
|
||||||
CredentialFormSchemaTextInput,
|
|
||||||
CustomConfigurationModelFixedFields,
|
|
||||||
FormValue,
|
|
||||||
ModelLoadBalancingConfigEntry,
|
|
||||||
ModelProvider,
|
|
||||||
} from '../declarations'
|
|
||||||
import {
|
|
||||||
ConfigurationMethodEnum,
|
|
||||||
FormTypeEnum,
|
|
||||||
} from '../declarations'
|
|
||||||
|
|
||||||
import {
|
|
||||||
useLanguage,
|
|
||||||
} from '../hooks'
|
|
||||||
import { useValidate } from '../../key-validator/hooks'
|
|
||||||
import { ValidatedStatus } from '../../key-validator/declarations'
|
|
||||||
import { validateLoadBalancingCredentials } from '../utils'
|
|
||||||
import Form from './Form'
|
|
||||||
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'
|
|
||||||
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'
|
|
||||||
|
|
||||||
type ModelModalProps = {
|
|
||||||
provider: ModelProvider
|
|
||||||
configurationMethod: ConfigurationMethodEnum
|
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
|
||||||
entry?: ModelLoadBalancingConfigEntry
|
|
||||||
onCancel: () => void
|
|
||||||
onSave: (entry: ModelLoadBalancingConfigEntry) => void
|
|
||||||
onRemove: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModelLoadBalancingEntryModal: FC<ModelModalProps> = ({
|
|
||||||
provider,
|
|
||||||
configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields,
|
|
||||||
entry,
|
|
||||||
onCancel,
|
|
||||||
onSave,
|
|
||||||
onRemove,
|
|
||||||
}) => {
|
|
||||||
const providerFormSchemaPredefined = configurationMethod === ConfigurationMethodEnum.predefinedModel
|
|
||||||
// const { credentials: formSchemasValue } = useProviderCredentialsAndLoadBalancing(
|
|
||||||
// provider.provider,
|
|
||||||
// configurationMethod,
|
|
||||||
// providerFormSchemaPredefined && provider.custom_configuration.status === CustomConfigurationStatusEnum.active,
|
|
||||||
// currentCustomConfigurationModelFixedFields,
|
|
||||||
// )
|
|
||||||
const isEditMode = !!entry
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { notify } = useToastContext()
|
|
||||||
const language = useLanguage()
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [showConfirm, setShowConfirm] = useState(false)
|
|
||||||
const formSchemas = useMemo(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
type: FormTypeEnum.textInput,
|
|
||||||
label: {
|
|
||||||
en_US: 'Config Name',
|
|
||||||
zh_Hans: '配置名称',
|
|
||||||
},
|
|
||||||
variable: 'name',
|
|
||||||
required: true,
|
|
||||||
show_on: [],
|
|
||||||
placeholder: {
|
|
||||||
en_US: 'Enter your Config Name here',
|
|
||||||
zh_Hans: '输入配置名称',
|
|
||||||
},
|
|
||||||
} as CredentialFormSchemaTextInput,
|
|
||||||
...(
|
|
||||||
providerFormSchemaPredefined
|
|
||||||
? provider.provider_credential_schema.credential_form_schemas
|
|
||||||
: provider.model_credential_schema.credential_form_schemas
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
providerFormSchemaPredefined,
|
|
||||||
provider.provider_credential_schema?.credential_form_schemas,
|
|
||||||
provider.model_credential_schema?.credential_form_schemas,
|
|
||||||
])
|
|
||||||
|
|
||||||
const [
|
|
||||||
requiredFormSchemas,
|
|
||||||
secretFormSchemas,
|
|
||||||
defaultFormSchemaValue,
|
|
||||||
showOnVariableMap,
|
|
||||||
] = useMemo(() => {
|
|
||||||
const requiredFormSchemas: CredentialFormSchema[] = []
|
|
||||||
const secretFormSchemas: CredentialFormSchema[] = []
|
|
||||||
const defaultFormSchemaValue: Record<string, string | number> = {}
|
|
||||||
const showOnVariableMap: Record<string, string[]> = {}
|
|
||||||
|
|
||||||
formSchemas.forEach((formSchema) => {
|
|
||||||
if (formSchema.required)
|
|
||||||
requiredFormSchemas.push(formSchema)
|
|
||||||
|
|
||||||
if (formSchema.type === FormTypeEnum.secretInput)
|
|
||||||
secretFormSchemas.push(formSchema)
|
|
||||||
|
|
||||||
if (formSchema.default)
|
|
||||||
defaultFormSchemaValue[formSchema.variable] = formSchema.default
|
|
||||||
|
|
||||||
if (formSchema.show_on.length) {
|
|
||||||
formSchema.show_on.forEach((showOnItem) => {
|
|
||||||
if (!showOnVariableMap[showOnItem.variable])
|
|
||||||
showOnVariableMap[showOnItem.variable] = []
|
|
||||||
|
|
||||||
if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable))
|
|
||||||
showOnVariableMap[showOnItem.variable].push(formSchema.variable)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formSchema.type === FormTypeEnum.select || formSchema.type === FormTypeEnum.radio) {
|
|
||||||
(formSchema as (CredentialFormSchemaRadio | CredentialFormSchemaSelect)).options.forEach((option) => {
|
|
||||||
if (option.show_on.length) {
|
|
||||||
option.show_on.forEach((showOnItem) => {
|
|
||||||
if (!showOnVariableMap[showOnItem.variable])
|
|
||||||
showOnVariableMap[showOnItem.variable] = []
|
|
||||||
|
|
||||||
if (!showOnVariableMap[showOnItem.variable].includes(formSchema.variable))
|
|
||||||
showOnVariableMap[showOnItem.variable].push(formSchema.variable)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [
|
|
||||||
requiredFormSchemas,
|
|
||||||
secretFormSchemas,
|
|
||||||
defaultFormSchemaValue,
|
|
||||||
showOnVariableMap,
|
|
||||||
]
|
|
||||||
}, [formSchemas])
|
|
||||||
const [initialValue, setInitialValue] = useState<ModelLoadBalancingConfigEntry['credentials']>()
|
|
||||||
useEffect(() => {
|
|
||||||
if (entry && !initialValue) {
|
|
||||||
setInitialValue({
|
|
||||||
...defaultFormSchemaValue,
|
|
||||||
...entry.credentials,
|
|
||||||
id: entry.id,
|
|
||||||
name: entry.name,
|
|
||||||
} as Record<string, string | undefined | boolean>)
|
|
||||||
}
|
|
||||||
}, [entry, defaultFormSchemaValue, initialValue])
|
|
||||||
const formSchemasValue = useMemo(() => ({
|
|
||||||
...currentCustomConfigurationModelFixedFields,
|
|
||||||
...initialValue,
|
|
||||||
}), [currentCustomConfigurationModelFixedFields, initialValue])
|
|
||||||
const initialFormSchemasValue: Record<string, string | number> = useMemo(() => {
|
|
||||||
return {
|
|
||||||
...defaultFormSchemaValue,
|
|
||||||
...formSchemasValue,
|
|
||||||
} as Record<string, string | number>
|
|
||||||
}, [formSchemasValue, defaultFormSchemaValue])
|
|
||||||
const [value, setValue] = useState(initialFormSchemasValue)
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialFormSchemasValue)
|
|
||||||
}, [initialFormSchemasValue])
|
|
||||||
const [_, validating, validatedStatusState] = useValidate(value)
|
|
||||||
const filteredRequiredFormSchemas = requiredFormSchemas.filter((requiredFormSchema) => {
|
|
||||||
if (requiredFormSchema.show_on.length && requiredFormSchema.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value))
|
|
||||||
return true
|
|
||||||
|
|
||||||
if (!requiredFormSchema.show_on.length)
|
|
||||||
return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
const getSecretValues = useCallback((v: FormValue) => {
|
|
||||||
return secretFormSchemas.reduce((prev, next) => {
|
|
||||||
if (isEditMode && v[next.variable] && v[next.variable] === initialFormSchemasValue[next.variable])
|
|
||||||
prev[next.variable] = '[__HIDDEN__]'
|
|
||||||
|
|
||||||
return prev
|
|
||||||
}, {} as Record<string, string>)
|
|
||||||
}, [initialFormSchemasValue, isEditMode, secretFormSchemas])
|
|
||||||
|
|
||||||
// const handleValueChange = ({ __model_type, __model_name, ...v }: FormValue) => {
|
|
||||||
const handleValueChange = (v: FormValue) => {
|
|
||||||
setValue(v)
|
|
||||||
}
|
|
||||||
const handleSave = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true)
|
|
||||||
|
|
||||||
const res = await validateLoadBalancingCredentials(
|
|
||||||
providerFormSchemaPredefined,
|
|
||||||
provider.provider,
|
|
||||||
{
|
|
||||||
...value,
|
|
||||||
...getSecretValues(value),
|
|
||||||
},
|
|
||||||
entry?.id,
|
|
||||||
)
|
|
||||||
if (res.status === ValidatedStatus.Success) {
|
|
||||||
// notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
|
||||||
const { __model_type, __model_name, name, ...credentials } = value
|
|
||||||
onSave({
|
|
||||||
...(entry || {}),
|
|
||||||
name: name as string,
|
|
||||||
credentials: credentials as Record<string, string | boolean | undefined>,
|
|
||||||
})
|
|
||||||
// onCancel()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
notify({ type: 'error', message: res.message || '' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRemove = () => {
|
|
||||||
onRemove?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PortalToFollowElem open>
|
|
||||||
<PortalToFollowElemContent className='z-[60] h-full w-full'>
|
|
||||||
<div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
|
|
||||||
<div className='mx-2 max-h-[calc(100vh-120px)] w-[640px] overflow-y-auto rounded-2xl bg-white shadow-xl'>
|
|
||||||
<div className='px-8 pt-8'>
|
|
||||||
<div className='mb-2 flex items-center justify-between'>
|
|
||||||
<div className='text-xl font-semibold text-gray-900'>{t(isEditMode ? 'common.modelProvider.editConfig' : 'common.modelProvider.addConfig')}</div>
|
|
||||||
</div>
|
|
||||||
<Form
|
|
||||||
value={value}
|
|
||||||
onChange={handleValueChange}
|
|
||||||
formSchemas={formSchemas}
|
|
||||||
validating={validating}
|
|
||||||
validatedSuccess={validatedStatusState.status === ValidatedStatus.Success}
|
|
||||||
showOnVariableMap={showOnVariableMap}
|
|
||||||
isEditMode={isEditMode}
|
|
||||||
/>
|
|
||||||
<div className='sticky bottom-0 flex flex-wrap items-center justify-between gap-y-2 bg-white py-6'>
|
|
||||||
{
|
|
||||||
(provider.help && (provider.help.title || provider.help.url))
|
|
||||||
? (
|
|
||||||
<a
|
|
||||||
href={provider.help?.url[language] || provider.help?.url.en_US}
|
|
||||||
target='_blank' rel='noopener noreferrer'
|
|
||||||
className='inline-flex items-center text-xs text-primary-600'
|
|
||||||
onClick={e => !provider.help.url && e.preventDefault()}
|
|
||||||
>
|
|
||||||
{provider.help.title?.[language] || provider.help.url[language] || provider.help.title?.en_US || provider.help.url.en_US}
|
|
||||||
<LinkExternal02 className='ml-1 h-3 w-3' />
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
: <div />
|
|
||||||
}
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
isEditMode && (
|
|
||||||
<Button
|
|
||||||
size='large'
|
|
||||||
className='mr-2 text-[#D92D20]'
|
|
||||||
onClick={() => setShowConfirm(true)}
|
|
||||||
>
|
|
||||||
{t('common.operation.remove')}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Button
|
|
||||||
size='large'
|
|
||||||
className='mr-2'
|
|
||||||
onClick={onCancel}
|
|
||||||
>
|
|
||||||
{t('common.operation.cancel')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size='large'
|
|
||||||
variant='primary'
|
|
||||||
onClick={handleSave}
|
|
||||||
disabled={loading || filteredRequiredFormSchemas.some(item => value[item.variable] === undefined)}
|
|
||||||
>
|
|
||||||
{t('common.operation.save')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='border-t-[0.5px] border-t-black/5'>
|
|
||||||
{
|
|
||||||
(validatedStatusState.status === ValidatedStatus.Error && validatedStatusState.message)
|
|
||||||
? (
|
|
||||||
<div className='flex bg-[#FEF3F2] px-[10px] py-3 text-xs text-[#D92D20]'>
|
|
||||||
<RiErrorWarningFill className='mr-2 mt-[1px] h-[14px] w-[14px]' />
|
|
||||||
{validatedStatusState.message}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<div className='flex items-center justify-center bg-gray-50 py-3 text-xs text-gray-500'>
|
|
||||||
<Lock01 className='mr-1 h-3 w-3 text-gray-500' />
|
|
||||||
{t('common.modelProvider.encrypted.front')}
|
|
||||||
<a
|
|
||||||
className='mx-1 text-primary-600'
|
|
||||||
target='_blank' rel='noopener noreferrer'
|
|
||||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
|
||||||
>
|
|
||||||
PKCS1_OAEP
|
|
||||||
</a>
|
|
||||||
{t('common.modelProvider.encrypted.back')}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
showConfirm && (
|
|
||||||
<Confirm
|
|
||||||
title={t('common.modelProvider.confirmDelete')}
|
|
||||||
isShow={showConfirm}
|
|
||||||
onCancel={() => setShowConfirm(false)}
|
|
||||||
onConfirm={handleRemove}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</PortalToFollowElemContent>
|
|
||||||
</PortalToFollowElem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(ModelLoadBalancingEntryModal)
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
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 type {
|
||||||
import type { ModelProvider } from '../declarations'
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
import {
|
import {
|
||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
CustomConfigurationStatusEnum,
|
CustomConfigurationStatusEnum,
|
||||||
@@ -15,19 +16,19 @@ import PrioritySelector from './priority-selector'
|
|||||||
import PriorityUseTip from './priority-use-tip'
|
import PriorityUseTip from './priority-use-tip'
|
||||||
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './index'
|
import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './index'
|
||||||
import Indicator from '@/app/components/header/indicator'
|
import Indicator from '@/app/components/header/indicator'
|
||||||
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 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 = {
|
type CredentialPanelProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
onSetup: () => void
|
|
||||||
}
|
}
|
||||||
const CredentialPanel: FC<CredentialPanelProps> = ({
|
const CredentialPanel = ({
|
||||||
provider,
|
provider,
|
||||||
onSetup,
|
}: CredentialPanelProps) => {
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
@@ -38,6 +39,12 @@ 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 {
|
||||||
|
hasCredential,
|
||||||
|
authorized,
|
||||||
|
authRemoved,
|
||||||
|
current_credential_name,
|
||||||
|
} = useCredentialStatus(provider)
|
||||||
|
|
||||||
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
const handleChangePriority = async (key: PreferredProviderTypeEnum) => {
|
||||||
const res = await changeModelProviderPriority({
|
const res = await changeModelProviderPriority({
|
||||||
@@ -61,25 +68,42 @@ const CredentialPanel: FC<CredentialPanelProps> = ({
|
|||||||
} as any)
|
} as any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const credentialLabel = useMemo(() => {
|
||||||
|
if (!hasCredential)
|
||||||
|
return t('common.modelProvider.auth.unAuthorized')
|
||||||
|
if (authorized)
|
||||||
|
return current_credential_name
|
||||||
|
if (authRemoved)
|
||||||
|
return t('common.modelProvider.auth.authRemoved')
|
||||||
|
|
||||||
|
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(
|
||||||
<div className='system-xs-medium-uppercase mb-1 flex h-5 items-center justify-between pl-2 pr-[7px] pt-1 text-text-tertiary'>
|
'relative ml-1 w-[120px] shrink-0 rounded-lg border-[0.5px] border-components-panel-border bg-white/[0.18] p-1',
|
||||||
API-KEY
|
authRemoved && 'border-state-destructive-border bg-state-destructive-hover',
|
||||||
<Indicator color={isCustomConfigured ? 'green' : 'red'} />
|
)}>
|
||||||
|
<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={cn(
|
||||||
|
'grow truncate',
|
||||||
|
authRemoved && 'text-text-destructive',
|
||||||
|
)}
|
||||||
|
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
|
<ConfigProvider
|
||||||
className='grow'
|
provider={provider}
|
||||||
size='small'
|
configurationMethod={ConfigurationMethodEnum.predefinedModel}
|
||||||
onClick={onSetup}
|
/>
|
||||||
>
|
|
||||||
<RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{t('common.operation.setup')}
|
|
||||||
</Button>
|
|
||||||
{
|
{
|
||||||
systemConfig.enabled && isCustomConfigured && (
|
systemConfig.enabled && isCustomConfigured && (
|
||||||
<PrioritySelector
|
<PrioritySelector
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
RiLoader2Line,
|
RiLoader2Line,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import type {
|
import type {
|
||||||
CustomConfigurationModelFixedFields,
|
|
||||||
ModelItem,
|
ModelItem,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
@@ -21,23 +20,21 @@ import ModelBadge from '../model-badge'
|
|||||||
import CredentialPanel from './credential-panel'
|
import CredentialPanel from './credential-panel'
|
||||||
import QuotaPanel from './quota-panel'
|
import QuotaPanel from './quota-panel'
|
||||||
import ModelList from './model-list'
|
import ModelList from './model-list'
|
||||||
import AddModelButton from './add-model-button'
|
|
||||||
import { fetchModelProviderModelList } from '@/service/common'
|
import { fetchModelProviderModelList } from '@/service/common'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
import { AddCustomModel } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
||||||
notConfigured,
|
notConfigured,
|
||||||
provider,
|
provider,
|
||||||
onOpenModal,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
@@ -114,7 +111,6 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
|||||||
{
|
{
|
||||||
showCredential && (
|
showCredential && (
|
||||||
<CredentialPanel
|
<CredentialPanel
|
||||||
onSetup={() => onOpenModal(ConfigurationMethodEnum.predefinedModel)}
|
|
||||||
provider={provider}
|
provider={provider}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -159,9 +155,9 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
|||||||
)}
|
)}
|
||||||
{
|
{
|
||||||
configurationMethods.includes(ConfigurationMethodEnum.customizableModel) && isCurrentWorkspaceManager && (
|
configurationMethods.includes(ConfigurationMethodEnum.customizableModel) && isCurrentWorkspaceManager && (
|
||||||
<AddModelButton
|
<AddCustomModel
|
||||||
onClick={() => onOpenModal(ConfigurationMethodEnum.customizableModel)}
|
provider={provider}
|
||||||
className='flex'
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -174,7 +170,6 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
|||||||
provider={provider}
|
provider={provider}
|
||||||
models={modelList}
|
models={modelList}
|
||||||
onCollapse={() => setCollapsed(true)}
|
onCollapse={() => setCollapsed(true)}
|
||||||
onConfig={currentCustomConfigurationModelFixedFields => onOpenModal(ConfigurationMethodEnum.customizableModel, currentCustomConfigurationModelFixedFields)}
|
|
||||||
onChange={(provider: string) => getModelList(provider)}
|
onChange={(provider: string) => getModelList(provider)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDebounceFn } from 'ahooks'
|
import { useDebounceFn } from 'ahooks'
|
||||||
import type { CustomConfigurationModelFixedFields, ModelItem, ModelProvider } from '../declarations'
|
import type { ModelItem, ModelProvider } from '../declarations'
|
||||||
import { ConfigurationMethodEnum, ModelStatusEnum } from '../declarations'
|
import { ModelStatusEnum } from '../declarations'
|
||||||
import ModelBadge from '../model-badge'
|
import ModelBadge from '../model-badge'
|
||||||
import ModelIcon from '../model-icon'
|
import ModelIcon from '../model-icon'
|
||||||
import ModelName from '../model-name'
|
import ModelName from '../model-name'
|
||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import { useProviderContext, useProviderContextSelector } from '@/context/provider-context'
|
import { useProviderContext, useProviderContextSelector } from '@/context/provider-context'
|
||||||
import { disableModel, enableModel } from '@/service/common'
|
import { disableModel, enableModel } from '@/service/common'
|
||||||
import { Plan } from '@/app/components/billing/type'
|
import { Plan } from '@/app/components/billing/type'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
|
import { ConfigModel } from '../model-auth'
|
||||||
|
|
||||||
export type ModelListItemProps = {
|
export type ModelListItemProps = {
|
||||||
model: ModelItem
|
model: ModelItem
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
isConfigurable: boolean
|
isConfigurable: boolean
|
||||||
onConfig: (currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => void
|
|
||||||
onModifyLoadBalancing?: (model: ModelItem) => void
|
onModifyLoadBalancing?: (model: ModelItem) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModelListItem = ({ model, provider, isConfigurable, onConfig, onModifyLoadBalancing }: ModelListItemProps) => {
|
const ModelListItem = ({ model, provider, isConfigurable, onModifyLoadBalancing }: ModelListItemProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { plan } = useProviderContext()
|
const { plan } = useProviderContext()
|
||||||
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
||||||
@@ -46,7 +44,7 @@ const ModelListItem = ({ model, provider, isConfigurable, onConfig, onModifyLoad
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={model.model}
|
key={`${model.model}-${model.fetch_from}`}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'group flex h-8 items-center rounded-lg pl-2 pr-2.5',
|
'group flex h-8 items-center rounded-lg pl-2 pr-2.5',
|
||||||
isConfigurable && 'hover:bg-components-panel-on-panel-item-bg-hover',
|
isConfigurable && 'hover:bg-components-panel-on-panel-item-bg-hover',
|
||||||
@@ -74,29 +72,12 @@ const ModelListItem = ({ model, provider, isConfigurable, onConfig, onModifyLoad
|
|||||||
</ModelName>
|
</ModelName>
|
||||||
<div className='flex shrink-0 items-center'>
|
<div className='flex shrink-0 items-center'>
|
||||||
{
|
{
|
||||||
model.fetch_from === ConfigurationMethodEnum.customizableModel
|
(isCurrentWorkspaceManager && (modelLoadBalancingEnabled || plan.type === Plan.sandbox) && !model.deprecated && [ModelStatusEnum.active, ModelStatusEnum.disabled].includes(model.status)) && (
|
||||||
? (isCurrentWorkspaceManager && (
|
<ConfigModel
|
||||||
<Button
|
className='hidden group-hover:flex'
|
||||||
size='small'
|
onClick={() => onModifyLoadBalancing?.(model)}
|
||||||
className='hidden group-hover:flex'
|
/>
|
||||||
onClick={() => onConfig({ __model_name: model.model, __model_type: model.model_type })}
|
)
|
||||||
>
|
|
||||||
<Settings01 className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{t('common.modelProvider.config')}
|
|
||||||
</Button>
|
|
||||||
))
|
|
||||||
: (isCurrentWorkspaceManager && (modelLoadBalancingEnabled || plan.type === Plan.sandbox) && !model.deprecated && [ModelStatusEnum.active, ModelStatusEnum.disabled].includes(model.status))
|
|
||||||
? (
|
|
||||||
<Button
|
|
||||||
size='small'
|
|
||||||
className='opacity-0 transition-opacity group-hover:opacity-100'
|
|
||||||
onClick={() => onModifyLoadBalancing?.(model)}
|
|
||||||
>
|
|
||||||
<Balance className='mr-1 h-3.5 w-3.5' />
|
|
||||||
{t('common.modelProvider.configLoadBalancing')}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
model.deprecated
|
model.deprecated
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
RiArrowRightSLine,
|
RiArrowRightSLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import type {
|
import type {
|
||||||
CustomConfigurationModelFixedFields,
|
Credential,
|
||||||
ModelItem,
|
ModelItem,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
@@ -13,34 +13,33 @@ import {
|
|||||||
ConfigurationMethodEnum,
|
ConfigurationMethodEnum,
|
||||||
} from '../declarations'
|
} from '../declarations'
|
||||||
// import Tab from './tab'
|
// import Tab from './tab'
|
||||||
import AddModelButton from './add-model-button'
|
|
||||||
import ModelListItem from './model-list-item'
|
import ModelListItem from './model-list-item'
|
||||||
import { useModalContextSelector } from '@/context/modal-context'
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
|
import { AddCustomModel } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
|
||||||
type ModelListProps = {
|
type ModelListProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
models: ModelItem[]
|
models: ModelItem[]
|
||||||
onCollapse: () => void
|
onCollapse: () => void
|
||||||
onConfig: (currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => void
|
|
||||||
onChange?: (provider: string) => void
|
onChange?: (provider: string) => void
|
||||||
}
|
}
|
||||||
const ModelList: FC<ModelListProps> = ({
|
const ModelList: FC<ModelListProps> = ({
|
||||||
provider,
|
provider,
|
||||||
models,
|
models,
|
||||||
onCollapse,
|
onCollapse,
|
||||||
onConfig,
|
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const configurativeMethods = provider.configurate_methods.filter(method => method !== ConfigurationMethodEnum.fetchFromRemote)
|
const configurativeMethods = provider.configurate_methods.filter(method => method !== ConfigurationMethodEnum.fetchFromRemote)
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
const isConfigurable = configurativeMethods.includes(ConfigurationMethodEnum.customizableModel)
|
const isConfigurable = configurativeMethods.includes(ConfigurationMethodEnum.customizableModel)
|
||||||
|
|
||||||
const setShowModelLoadBalancingModal = useModalContextSelector(state => state.setShowModelLoadBalancingModal)
|
const setShowModelLoadBalancingModal = useModalContextSelector(state => state.setShowModelLoadBalancingModal)
|
||||||
const onModifyLoadBalancing = useCallback((model: ModelItem) => {
|
const onModifyLoadBalancing = useCallback((model: ModelItem, credential?: Credential) => {
|
||||||
setShowModelLoadBalancingModal({
|
setShowModelLoadBalancingModal({
|
||||||
provider,
|
provider,
|
||||||
|
credential,
|
||||||
|
configurateMethod: model.fetch_from,
|
||||||
model: model!,
|
model: model!,
|
||||||
open: !!model,
|
open: !!model,
|
||||||
onClose: () => setShowModelLoadBalancingModal(null),
|
onClose: () => setShowModelLoadBalancingModal(null),
|
||||||
@@ -65,17 +64,14 @@ const ModelList: FC<ModelListProps> = ({
|
|||||||
<RiArrowRightSLine className='mr-0.5 h-4 w-4 rotate-90' />
|
<RiArrowRightSLine className='mr-0.5 h-4 w-4 rotate-90' />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{/* {
|
|
||||||
isConfigurable && canSystemConfig && (
|
|
||||||
<span className='flex items-center'>
|
|
||||||
<Tab active='all' onSelect={() => {}} />
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
} */}
|
|
||||||
{
|
{
|
||||||
isConfigurable && isCurrentWorkspaceManager && (
|
isConfigurable && isCurrentWorkspaceManager && (
|
||||||
<div className='flex grow justify-end'>
|
<div className='flex grow justify-end'>
|
||||||
<AddModelButton onClick={() => onConfig()} />
|
<AddCustomModel
|
||||||
|
provider={provider}
|
||||||
|
configurationMethod={ConfigurationMethodEnum.customizableModel}
|
||||||
|
currentCustomConfigurationModelFixedFields={undefined}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -83,12 +79,11 @@ const ModelList: FC<ModelListProps> = ({
|
|||||||
{
|
{
|
||||||
models.map(model => (
|
models.map(model => (
|
||||||
<ModelListItem
|
<ModelListItem
|
||||||
key={model.model}
|
key={`${model.model}-${model.fetch_from}`}
|
||||||
{...{
|
{...{
|
||||||
model,
|
model,
|
||||||
provider,
|
provider,
|
||||||
isConfigurable,
|
isConfigurable,
|
||||||
onConfig,
|
|
||||||
onModifyLoadBalancing,
|
onModifyLoadBalancing,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,22 +3,32 @@ import { useCallback } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
RiDeleteBinLine,
|
RiDeleteBinLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import type { ConfigurationMethodEnum, CustomConfigurationModelFixedFields, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations'
|
import type {
|
||||||
|
Credential,
|
||||||
|
CustomConfigurationModelFixedFields,
|
||||||
|
CustomModelCredential,
|
||||||
|
ModelCredential,
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelLoadBalancingConfigEntry,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
|
import { ConfigurationMethodEnum } from '../declarations'
|
||||||
import Indicator from '../../../indicator'
|
import Indicator from '../../../indicator'
|
||||||
import CooldownTimer from './cooldown-timer'
|
import CooldownTimer from './cooldown-timer'
|
||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||||
import { Edit02, Plus02 } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
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'
|
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||||
import s from '@/app/components/custom/style.module.css'
|
import s from '@/app/components/custom/style.module.css'
|
||||||
import GridMask from '@/app/components/base/grid-mask'
|
import GridMask from '@/app/components/base/grid-mask'
|
||||||
import { useProviderContextSelector } from '@/context/provider-context'
|
import { useProviderContextSelector } from '@/context/provider-context'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
|
import { AddCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
import { useModelModalHandler } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
|
|
||||||
export type ModelLoadBalancingConfigsProps = {
|
export type ModelLoadBalancingConfigsProps = {
|
||||||
draftConfig?: ModelLoadBalancingConfig
|
draftConfig?: ModelLoadBalancingConfig
|
||||||
@@ -28,19 +38,26 @@ export type ModelLoadBalancingConfigsProps = {
|
|||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
withSwitch?: boolean
|
withSwitch?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
|
modelCredential: ModelCredential
|
||||||
|
onUpdate?: () => void
|
||||||
|
model: CustomModelCredential
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModelLoadBalancingConfigs = ({
|
const ModelLoadBalancingConfigs = ({
|
||||||
draftConfig,
|
draftConfig,
|
||||||
setDraftConfig,
|
setDraftConfig,
|
||||||
provider,
|
provider,
|
||||||
|
model,
|
||||||
configurationMethod,
|
configurationMethod,
|
||||||
currentCustomConfigurationModelFixedFields,
|
currentCustomConfigurationModelFixedFields,
|
||||||
withSwitch = false,
|
withSwitch = false,
|
||||||
className,
|
className,
|
||||||
|
modelCredential,
|
||||||
|
onUpdate,
|
||||||
}: ModelLoadBalancingConfigsProps) => {
|
}: ModelLoadBalancingConfigsProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
||||||
|
const handleOpenModal = useModelModalHandler()
|
||||||
|
|
||||||
const updateConfigEntry = useCallback(
|
const updateConfigEntry = useCallback(
|
||||||
(
|
(
|
||||||
@@ -65,6 +82,21 @@ const ModelLoadBalancingConfigs = ({
|
|||||||
[setDraftConfig],
|
[setDraftConfig],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const addConfigEntry = useCallback((credential: Credential) => {
|
||||||
|
setDraftConfig((prev: any) => {
|
||||||
|
if (!prev)
|
||||||
|
return prev
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
configs: [...prev.configs, {
|
||||||
|
credential_id: credential.credential_id,
|
||||||
|
enabled: true,
|
||||||
|
name: credential.credential_name,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [setDraftConfig])
|
||||||
|
|
||||||
const toggleModalBalancing = useCallback((enabled: boolean) => {
|
const toggleModalBalancing = useCallback((enabled: boolean) => {
|
||||||
if ((modelLoadBalancingEnabled || !enabled) && draftConfig) {
|
if ((modelLoadBalancingEnabled || !enabled) && draftConfig) {
|
||||||
setDraftConfig({
|
setDraftConfig({
|
||||||
@@ -81,54 +113,6 @@ const ModelLoadBalancingConfigs = ({
|
|||||||
}))
|
}))
|
||||||
}, [updateConfigEntry])
|
}, [updateConfigEntry])
|
||||||
|
|
||||||
const setShowModelLoadBalancingEntryModal = useModalContextSelector(state => state.setShowModelLoadBalancingEntryModal)
|
|
||||||
|
|
||||||
const toggleEntryModal = useCallback((index?: number, entry?: ModelLoadBalancingConfigEntry) => {
|
|
||||||
setShowModelLoadBalancingEntryModal({
|
|
||||||
payload: {
|
|
||||||
currentProvider: provider,
|
|
||||||
currentConfigurationMethod: configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields,
|
|
||||||
entry,
|
|
||||||
index,
|
|
||||||
},
|
|
||||||
onSaveCallback: ({ entry: result }) => {
|
|
||||||
if (entry) {
|
|
||||||
// edit
|
|
||||||
setDraftConfig(prev => ({
|
|
||||||
...prev,
|
|
||||||
enabled: !!prev?.enabled,
|
|
||||||
configs: prev?.configs.map((config, i) => i === index ? result! : config) || [],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// add
|
|
||||||
setDraftConfig(prev => ({
|
|
||||||
...prev,
|
|
||||||
enabled: !!prev?.enabled,
|
|
||||||
configs: (prev?.configs || []).concat([{ ...result!, enabled: true }]),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRemoveCallback: ({ index }) => {
|
|
||||||
if (index !== undefined && (draftConfig?.configs?.length ?? 0) > index) {
|
|
||||||
setDraftConfig(prev => ({
|
|
||||||
...prev,
|
|
||||||
enabled: !!prev?.enabled,
|
|
||||||
configs: prev?.configs.filter((_, i) => i !== index) || [],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}, [
|
|
||||||
configurationMethod,
|
|
||||||
currentCustomConfigurationModelFixedFields,
|
|
||||||
draftConfig?.configs?.length,
|
|
||||||
provider,
|
|
||||||
setDraftConfig,
|
|
||||||
setShowModelLoadBalancingEntryModal,
|
|
||||||
])
|
|
||||||
|
|
||||||
const clearCountdown = useCallback((index: number) => {
|
const clearCountdown = useCallback((index: number) => {
|
||||||
updateConfigEntry(index, ({ ttl: _, ...entry }) => {
|
updateConfigEntry(index, ({ ttl: _, ...entry }) => {
|
||||||
return {
|
return {
|
||||||
@@ -210,9 +194,21 @@ const ModelLoadBalancingConfigs = ({
|
|||||||
<div className='flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100'>
|
<div className='flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100'>
|
||||||
<span
|
<span
|
||||||
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
||||||
onClick={() => toggleEntryModal(index, config)}
|
onClick={() => {
|
||||||
|
handleOpenModal(
|
||||||
|
provider,
|
||||||
|
configurationMethod,
|
||||||
|
currentCustomConfigurationModelFixedFields,
|
||||||
|
configurationMethod === ConfigurationMethodEnum.customizableModel,
|
||||||
|
(config.credential_id && config.name) ? {
|
||||||
|
credential_id: config.credential_id,
|
||||||
|
credential_name: config.name,
|
||||||
|
} : undefined,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Edit02 className='h-4 w-4' />
|
<RiEqualizer2Line className='h-4 w-4' />
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg text-text-tertiary transition-colors hover:bg-components-button-secondary-bg-hover'
|
||||||
@@ -234,20 +230,19 @@ const ModelLoadBalancingConfigs = ({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
<AddCredentialInLoadBalancing
|
||||||
<div
|
provider={provider}
|
||||||
className='mt-1 flex h-8 items-center px-3 text-[13px] font-medium text-primary-600'
|
model={model}
|
||||||
onClick={() => toggleEntryModal()}
|
configurationMethod={configurationMethod}
|
||||||
>
|
modelCredential={modelCredential}
|
||||||
<div className='flex cursor-pointer items-center'>
|
onSelectCredential={addConfigEntry}
|
||||||
<Plus02 className='mr-2 h-3 w-3' />{t('common.modelProvider.addConfig')}
|
onUpdate={onUpdate}
|
||||||
</div>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{
|
{
|
||||||
draftConfig.enabled && draftConfig.configs.length < 2 && (
|
draftConfig.enabled && draftConfig.configs.length < 2 && (
|
||||||
<div className='flex h-[34px] items-center border-t border-t-divider-subtle bg-components-panel-bg px-6 text-xs text-text-secondary'>
|
<div className='flex h-[34px] items-center rounded-b-xl border-t border-t-divider-subtle bg-components-panel-bg px-6 text-xs text-text-secondary'>
|
||||||
<AlertTriangle className='mr-1 h-3 w-3 text-[#f79009]' />
|
<AlertTriangle className='mr-1 h-3 w-3 text-[#f79009]' />
|
||||||
{t('common.modelProvider.loadBalancingLeastKeyWarning')}
|
{t('common.modelProvider.loadBalancingLeastKeyWarning')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,40 +1,66 @@
|
|||||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useSWR from 'swr'
|
import type {
|
||||||
import type { ModelItem, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations'
|
Credential,
|
||||||
import { FormTypeEnum } from '../declarations'
|
ModelItem,
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelLoadBalancingConfigEntry,
|
||||||
|
ModelProvider,
|
||||||
|
} from '../declarations'
|
||||||
|
import {
|
||||||
|
ConfigurationMethodEnum,
|
||||||
|
FormTypeEnum,
|
||||||
|
} from '../declarations'
|
||||||
import ModelIcon from '../model-icon'
|
import ModelIcon from '../model-icon'
|
||||||
import ModelName from '../model-name'
|
import ModelName from '../model-name'
|
||||||
import { savePredefinedLoadBalancingConfig } from '../utils'
|
|
||||||
import ModelLoadBalancingConfigs from './model-load-balancing-configs'
|
import ModelLoadBalancingConfigs from './model-load-balancing-configs'
|
||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { fetchModelLoadBalancingConfig } from '@/service/common'
|
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
// import { SwitchCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||||
|
import {
|
||||||
|
useGetModelCredential,
|
||||||
|
useUpdateModelLoadBalancingConfig,
|
||||||
|
} from '@/service/use-models'
|
||||||
|
|
||||||
export type ModelLoadBalancingModalProps = {
|
export type ModelLoadBalancingModalProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
|
configurateMethod: ConfigurationMethodEnum
|
||||||
model: ModelItem
|
model: ModelItem
|
||||||
|
credential?: Credential
|
||||||
open?: boolean
|
open?: boolean
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
onSave?: (provider: string) => void
|
onSave?: (provider: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
// model balancing config modal
|
// model balancing config modal
|
||||||
const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSave }: ModelLoadBalancingModalProps) => {
|
const ModelLoadBalancingModal = ({
|
||||||
|
provider,
|
||||||
|
configurateMethod,
|
||||||
|
model,
|
||||||
|
credential,
|
||||||
|
open = false,
|
||||||
|
onClose,
|
||||||
|
onSave,
|
||||||
|
}: ModelLoadBalancingModalProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const providerFormSchemaPredefined = configurateMethod === ConfigurationMethodEnum.predefinedModel
|
||||||
const { data, mutate } = useSWR(
|
const configFrom = providerFormSchemaPredefined ? 'predefined-model' : 'custom-model'
|
||||||
`/workspaces/current/model-providers/${provider.provider}/models/credentials?model=${model.model}&model_type=${model.model_type}`,
|
const {
|
||||||
fetchModelLoadBalancingConfig,
|
isLoading,
|
||||||
)
|
data,
|
||||||
|
refetch,
|
||||||
const originalConfig = data?.load_balancing
|
} = useGetModelCredential(true, provider.provider, credential?.credential_id, model.model, model.model_type, configFrom)
|
||||||
|
const modelCredential = data
|
||||||
|
const {
|
||||||
|
load_balancing,
|
||||||
|
} = modelCredential ?? {}
|
||||||
|
const originalConfig = load_balancing
|
||||||
const [draftConfig, setDraftConfig] = useState<ModelLoadBalancingConfig>()
|
const [draftConfig, setDraftConfig] = useState<ModelLoadBalancingConfig>()
|
||||||
const originalConfigMap = useMemo(() => {
|
const originalConfigMap = useMemo(() => {
|
||||||
if (!originalConfig)
|
if (!originalConfig)
|
||||||
@@ -75,25 +101,24 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
|
|||||||
return result
|
return result
|
||||||
}, [extendedSecretFormSchemas, originalConfigMap])
|
}, [extendedSecretFormSchemas, originalConfigMap])
|
||||||
|
|
||||||
|
const { mutateAsync: updateModelLoadBalancingConfig } = useUpdateModelLoadBalancingConfig(provider.provider)
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const res = await savePredefinedLoadBalancingConfig(
|
const res = await updateModelLoadBalancingConfig(
|
||||||
provider.provider,
|
|
||||||
({
|
|
||||||
...(data?.credentials ?? {}),
|
|
||||||
__model_type: model.model_type,
|
|
||||||
__model_name: model.model,
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
...draftConfig,
|
config_from: configFrom,
|
||||||
enabled: Boolean(draftConfig?.enabled),
|
model: model.model,
|
||||||
configs: draftConfig!.configs.map(encodeConfigEntrySecretValues),
|
model_type: model.model_type,
|
||||||
|
load_balancing: {
|
||||||
|
...draftConfig,
|
||||||
|
configs: draftConfig!.configs.map(encodeConfigEntrySecretValues),
|
||||||
|
enabled: Boolean(draftConfig?.enabled),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
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()
|
|
||||||
onSave?.(provider.provider)
|
onSave?.(provider.provider)
|
||||||
onClose?.()
|
onClose?.()
|
||||||
}
|
}
|
||||||
@@ -110,7 +135,11 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
|
|||||||
className='w-[640px] max-w-none px-8 pt-8'
|
className='w-[640px] max-w-none px-8 pt-8'
|
||||||
title={
|
title={
|
||||||
<div className='pb-3 font-semibold'>
|
<div className='pb-3 font-semibold'>
|
||||||
<div className='h-[30px]'>{t('common.modelProvider.configLoadBalancing')}</div>
|
<div className='h-[30px]'>{
|
||||||
|
draftConfig?.enabled
|
||||||
|
? t('common.modelProvider.auth.configLoadBalancing')
|
||||||
|
: t('common.modelProvider.auth.configModel')
|
||||||
|
}</div>
|
||||||
{Boolean(model) && (
|
{Boolean(model) && (
|
||||||
<div className='flex h-5 items-center'>
|
<div className='flex h-5 items-center'>
|
||||||
<ModelIcon
|
<ModelIcon
|
||||||
@@ -152,20 +181,34 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
|
|||||||
<div className='text-sm text-text-secondary'>{t('common.modelProvider.providerManaged')}</div>
|
<div className='text-sm text-text-secondary'>{t('common.modelProvider.providerManaged')}</div>
|
||||||
<div className='text-xs text-text-tertiary'>{t('common.modelProvider.providerManagedDescription')}</div>
|
<div className='text-xs text-text-tertiary'>{t('common.modelProvider.providerManagedDescription')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* <SwitchCredentialInLoadBalancing
|
||||||
|
draftConfig={draftConfig}
|
||||||
|
setDraftConfig={setDraftConfig}
|
||||||
|
provider={provider}
|
||||||
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
<ModelLoadBalancingConfigs {...{
|
modelCredential && (
|
||||||
draftConfig,
|
<ModelLoadBalancingConfigs {...{
|
||||||
setDraftConfig,
|
draftConfig,
|
||||||
provider,
|
setDraftConfig,
|
||||||
currentCustomConfigurationModelFixedFields: {
|
provider,
|
||||||
__model_name: model.model,
|
currentCustomConfigurationModelFixedFields: {
|
||||||
__model_type: model.model_type,
|
__model_name: model.model,
|
||||||
},
|
__model_type: model.model_type,
|
||||||
configurationMethod: model.fetch_from,
|
},
|
||||||
className: 'mt-2',
|
configurationMethod: model.fetch_from,
|
||||||
}} />
|
className: 'mt-2',
|
||||||
|
modelCredential,
|
||||||
|
onUpdate: refetch,
|
||||||
|
model: {
|
||||||
|
model: model.model,
|
||||||
|
model_type: model.model_type,
|
||||||
|
},
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 flex items-center justify-end gap-2'>
|
<div className='mt-6 flex items-center justify-end gap-2'>
|
||||||
@@ -176,6 +219,7 @@ const ModelLoadBalancingModal = ({ provider, model, open = false, onClose, onSav
|
|||||||
disabled={
|
disabled={
|
||||||
loading
|
loading
|
||||||
|| (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
|
|| (draftConfig?.enabled && (draftConfig?.configs.filter(config => config.enabled).length ?? 0) < 2)
|
||||||
|
|| isLoading
|
||||||
}
|
}
|
||||||
>{t('common.operation.save')}</Button>
|
>{t('common.operation.save')}</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ValidatedStatus } from '../key-validator/declarations'
|
import { ValidatedStatus } from '../key-validator/declarations'
|
||||||
import type {
|
import type {
|
||||||
CredentialFormSchemaRadio,
|
|
||||||
CredentialFormSchemaTextInput,
|
CredentialFormSchemaTextInput,
|
||||||
FormValue,
|
FormValue,
|
||||||
ModelLoadBalancingConfig,
|
ModelLoadBalancingConfig,
|
||||||
@@ -82,12 +81,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 +118,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) {
|
||||||
@@ -174,7 +180,7 @@ export const genModelTypeFormSchema = (modelTypes: ModelTypeEnum[]) => {
|
|||||||
show_on: [],
|
show_on: [],
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
} as CredentialFormSchemaRadio
|
} as any
|
||||||
}
|
}
|
||||||
|
|
||||||
export const genModelNameFormSchema = (model?: Pick<CredentialFormSchemaTextInput, 'label' | 'placeholder'>) => {
|
export const genModelNameFormSchema = (model?: Pick<CredentialFormSchemaTextInput, 'label' | 'placeholder'>) => {
|
||||||
@@ -191,5 +197,5 @@ export const genModelNameFormSchema = (model?: Pick<CredentialFormSchemaTextInpu
|
|||||||
zh_Hans: '请输入模型名称',
|
zh_Hans: '请输入模型名称',
|
||||||
en_US: 'Please enter model name',
|
en_US: 'Please enter model name',
|
||||||
},
|
},
|
||||||
} as CredentialFormSchemaTextInput
|
} as any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,13 @@ const Item = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
credential.from_enterprise && (
|
||||||
|
<Badge className='shrink-0'>
|
||||||
|
Enterprise
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -172,7 +179,7 @@ const Item = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!isOAuth && !disableEdit && (
|
!isOAuth && !disableEdit && !credential.from_enterprise && (
|
||||||
<Tooltip popupContent={t('common.operation.edit')}>
|
<Tooltip popupContent={t('common.operation.edit')}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -194,7 +201,7 @@ const Item = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!disableDelete && (
|
!disableDelete && !credential.from_enterprise && (
|
||||||
<Tooltip popupContent={t('common.operation.delete')}>
|
<Tooltip popupContent={t('common.operation.delete')}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className='hover:bg-transparent'
|
className='hover:bg-transparent'
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
import type { PluginPayload } from '@/app/components/plugins/plugin-auth/types'
|
||||||
|
import {
|
||||||
|
useDeletePluginCredentialHook,
|
||||||
|
useSetPluginDefaultCredentialHook,
|
||||||
|
useUpdatePluginCredentialHook,
|
||||||
|
} from '../hooks/use-credential'
|
||||||
|
|
||||||
|
export const usePluginAuthAction = (
|
||||||
|
pluginPayload: PluginPayload,
|
||||||
|
onUpdate?: () => void,
|
||||||
|
) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { notify } = useToastContext()
|
||||||
|
const pendingOperationCredentialId = useRef<string | null>(null)
|
||||||
|
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||||
|
const { mutateAsync: deletePluginCredential } = useDeletePluginCredentialHook(pluginPayload)
|
||||||
|
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 [editValues, setEditValues] = useState<Record<string, any> | null>(null)
|
||||||
|
const handleConfirm = useCallback(async () => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
if (!pendingOperationCredentialId.current) {
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await deletePluginCredential({ credential_id: pendingOperationCredentialId.current })
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
setEditValues(null)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [deletePluginCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||||
|
const handleEdit = useCallback((id: string, values: Record<string, any>) => {
|
||||||
|
pendingOperationCredentialId.current = id
|
||||||
|
setEditValues(values)
|
||||||
|
}, [])
|
||||||
|
const handleRemove = useCallback(() => {
|
||||||
|
setDeleteCredentialId(pendingOperationCredentialId.current)
|
||||||
|
}, [])
|
||||||
|
const { mutateAsync: setPluginDefaultCredential } = useSetPluginDefaultCredentialHook(pluginPayload)
|
||||||
|
const handleSetDefault = useCallback(async (id: string) => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await setPluginDefaultCredential(id)
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [setPluginDefaultCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||||
|
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||||
|
const handleRename = useCallback(async (payload: {
|
||||||
|
credential_id: string
|
||||||
|
name: string
|
||||||
|
}) => {
|
||||||
|
if (doingActionRef.current)
|
||||||
|
return
|
||||||
|
try {
|
||||||
|
handleSetDoingAction(true)
|
||||||
|
await updatePluginCredential(payload)
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
onUpdate?.()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
handleSetDoingAction(false)
|
||||||
|
}
|
||||||
|
}, [updatePluginCredential, notify, t, handleSetDoingAction, onUpdate])
|
||||||
|
|
||||||
|
return {
|
||||||
|
doingAction,
|
||||||
|
handleSetDoingAction,
|
||||||
|
openConfirm,
|
||||||
|
closeConfirm,
|
||||||
|
deleteCredentialId,
|
||||||
|
setDeleteCredentialId,
|
||||||
|
handleConfirm,
|
||||||
|
editValues,
|
||||||
|
setEditValues,
|
||||||
|
handleEdit,
|
||||||
|
handleRemove,
|
||||||
|
handleSetDefault,
|
||||||
|
handleRename,
|
||||||
|
pendingOperationCredentialId,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,4 +22,5 @@ export type Credential = {
|
|||||||
is_default: boolean
|
is_default: boolean
|
||||||
credentials?: Record<string, any>
|
credentials?: Record<string, any>
|
||||||
isWorkspaceDefault?: boolean
|
isWorkspaceDefault?: boolean
|
||||||
|
from_enterprise?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ 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,
|
||||||
|
CustomModel,
|
||||||
ModelLoadBalancingConfigEntry,
|
ModelLoadBalancingConfigEntry,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
@@ -54,9 +56,6 @@ const ExternalAPIModal = dynamic(() => import('@/app/components/datasets/externa
|
|||||||
const ModelLoadBalancingModal = dynamic(() => import('@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'), {
|
const ModelLoadBalancingModal = dynamic(() => import('@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
})
|
})
|
||||||
const ModelLoadBalancingEntryModal = dynamic(() => import('@/app/components/header/account-setting/model-provider-page/model-modal/model-load-balancing-entry-modal'), {
|
|
||||||
ssr: false,
|
|
||||||
})
|
|
||||||
const OpeningSettingModal = dynamic(() => import('@/app/components/base/features/new-feature-panel/conversation-opener/modal'), {
|
const OpeningSettingModal = dynamic(() => import('@/app/components/base/features/new-feature-panel/conversation-opener/modal'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
})
|
})
|
||||||
@@ -79,6 +78,9 @@ export type ModelModalType = {
|
|||||||
currentProvider: ModelProvider
|
currentProvider: ModelProvider
|
||||||
currentConfigurationMethod: ConfigurationMethodEnum
|
currentConfigurationMethod: ConfigurationMethodEnum
|
||||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
|
||||||
|
isModelCredential?: boolean
|
||||||
|
credential?: Credential
|
||||||
|
model?: CustomModel
|
||||||
}
|
}
|
||||||
export type LoadBalancingEntryModalType = ModelModalType & {
|
export type LoadBalancingEntryModalType = ModelModalType & {
|
||||||
entry?: ModelLoadBalancingConfigEntry
|
entry?: ModelLoadBalancingConfigEntry
|
||||||
@@ -95,7 +97,6 @@ export type ModalContextState = {
|
|||||||
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
|
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
|
||||||
setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
|
setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
|
||||||
setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
|
setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
|
||||||
setShowModelLoadBalancingEntryModal: Dispatch<SetStateAction<ModalState<LoadBalancingEntryModalType> | null>>
|
|
||||||
setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & {
|
setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & {
|
||||||
promptVariables?: PromptVariable[]
|
promptVariables?: PromptVariable[]
|
||||||
workflowVariables?: InputVar[]
|
workflowVariables?: InputVar[]
|
||||||
@@ -113,7 +114,6 @@ const ModalContext = createContext<ModalContextState>({
|
|||||||
setShowModelModal: noop,
|
setShowModelModal: noop,
|
||||||
setShowExternalKnowledgeAPIModal: noop,
|
setShowExternalKnowledgeAPIModal: noop,
|
||||||
setShowModelLoadBalancingModal: noop,
|
setShowModelLoadBalancingModal: noop,
|
||||||
setShowModelLoadBalancingEntryModal: noop,
|
|
||||||
setShowOpeningModal: noop,
|
setShowOpeningModal: noop,
|
||||||
setShowUpdatePluginModal: noop,
|
setShowUpdatePluginModal: noop,
|
||||||
})
|
})
|
||||||
@@ -138,7 +138,6 @@ export const ModalContextProvider = ({
|
|||||||
const [showModelModal, setShowModelModal] = useState<ModalState<ModelModalType> | null>(null)
|
const [showModelModal, setShowModelModal] = useState<ModalState<ModelModalType> | null>(null)
|
||||||
const [showExternalKnowledgeAPIModal, setShowExternalKnowledgeAPIModal] = useState<ModalState<CreateExternalAPIReq> | null>(null)
|
const [showExternalKnowledgeAPIModal, setShowExternalKnowledgeAPIModal] = useState<ModalState<CreateExternalAPIReq> | null>(null)
|
||||||
const [showModelLoadBalancingModal, setShowModelLoadBalancingModal] = useState<ModelLoadBalancingModalProps | null>(null)
|
const [showModelLoadBalancingModal, setShowModelLoadBalancingModal] = useState<ModelLoadBalancingModalProps | null>(null)
|
||||||
const [showModelLoadBalancingEntryModal, setShowModelLoadBalancingEntryModal] = useState<ModalState<LoadBalancingEntryModalType> | null>(null)
|
|
||||||
const [showOpeningModal, setShowOpeningModal] = useState<ModalState<OpeningStatement & {
|
const [showOpeningModal, setShowOpeningModal] = useState<ModalState<OpeningStatement & {
|
||||||
promptVariables?: PromptVariable[]
|
promptVariables?: PromptVariable[]
|
||||||
workflowVariables?: InputVar[]
|
workflowVariables?: InputVar[]
|
||||||
@@ -204,30 +203,12 @@ export const ModalContextProvider = ({
|
|||||||
setShowExternalKnowledgeAPIModal(null)
|
setShowExternalKnowledgeAPIModal(null)
|
||||||
}, [showExternalKnowledgeAPIModal])
|
}, [showExternalKnowledgeAPIModal])
|
||||||
|
|
||||||
const handleCancelModelLoadBalancingEntryModal = useCallback(() => {
|
|
||||||
showModelLoadBalancingEntryModal?.onCancelCallback?.()
|
|
||||||
setShowModelLoadBalancingEntryModal(null)
|
|
||||||
}, [showModelLoadBalancingEntryModal])
|
|
||||||
|
|
||||||
const handleCancelOpeningModal = useCallback(() => {
|
const handleCancelOpeningModal = useCallback(() => {
|
||||||
setShowOpeningModal(null)
|
setShowOpeningModal(null)
|
||||||
if (showOpeningModal?.onCancelCallback)
|
if (showOpeningModal?.onCancelCallback)
|
||||||
showOpeningModal.onCancelCallback()
|
showOpeningModal.onCancelCallback()
|
||||||
}, [showOpeningModal])
|
}, [showOpeningModal])
|
||||||
|
|
||||||
const handleSaveModelLoadBalancingEntryModal = useCallback((entry: ModelLoadBalancingConfigEntry) => {
|
|
||||||
showModelLoadBalancingEntryModal?.onSaveCallback?.({
|
|
||||||
...showModelLoadBalancingEntryModal.payload,
|
|
||||||
entry,
|
|
||||||
})
|
|
||||||
setShowModelLoadBalancingEntryModal(null)
|
|
||||||
}, [showModelLoadBalancingEntryModal])
|
|
||||||
|
|
||||||
const handleRemoveModelLoadBalancingEntry = useCallback(() => {
|
|
||||||
showModelLoadBalancingEntryModal?.onRemoveCallback?.(showModelLoadBalancingEntryModal.payload)
|
|
||||||
setShowModelLoadBalancingEntryModal(null)
|
|
||||||
}, [showModelLoadBalancingEntryModal])
|
|
||||||
|
|
||||||
const handleSaveApiBasedExtension = (newApiBasedExtension: ApiBasedExtension) => {
|
const handleSaveApiBasedExtension = (newApiBasedExtension: ApiBasedExtension) => {
|
||||||
if (showApiBasedExtensionModal?.onSaveCallback)
|
if (showApiBasedExtensionModal?.onSaveCallback)
|
||||||
showApiBasedExtensionModal.onSaveCallback(newApiBasedExtension)
|
showApiBasedExtensionModal.onSaveCallback(newApiBasedExtension)
|
||||||
@@ -269,7 +250,6 @@ export const ModalContextProvider = ({
|
|||||||
setShowModelModal,
|
setShowModelModal,
|
||||||
setShowExternalKnowledgeAPIModal,
|
setShowExternalKnowledgeAPIModal,
|
||||||
setShowModelLoadBalancingModal,
|
setShowModelLoadBalancingModal,
|
||||||
setShowModelLoadBalancingEntryModal,
|
|
||||||
setShowOpeningModal,
|
setShowOpeningModal,
|
||||||
setShowUpdatePluginModal,
|
setShowUpdatePluginModal,
|
||||||
}}>
|
}}>
|
||||||
@@ -337,6 +317,9 @@ 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}
|
||||||
|
isModelCredential={showModelModal.payload.isModelCredential}
|
||||||
|
credential={showModelModal.payload.credential}
|
||||||
|
model={showModelModal.payload.model}
|
||||||
onCancel={handleCancelModelModal}
|
onCancel={handleCancelModelModal}
|
||||||
onSave={handleSaveModelModal}
|
onSave={handleSaveModelModal}
|
||||||
/>
|
/>
|
||||||
@@ -359,19 +342,6 @@ export const ModalContextProvider = ({
|
|||||||
<ModelLoadBalancingModal {...showModelLoadBalancingModal!} />
|
<ModelLoadBalancingModal {...showModelLoadBalancingModal!} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
!!showModelLoadBalancingEntryModal && (
|
|
||||||
<ModelLoadBalancingEntryModal
|
|
||||||
provider={showModelLoadBalancingEntryModal.payload.currentProvider}
|
|
||||||
configurationMethod={showModelLoadBalancingEntryModal.payload.currentConfigurationMethod}
|
|
||||||
currentCustomConfigurationModelFixedFields={showModelLoadBalancingEntryModal.payload.currentCustomConfigurationModelFixedFields}
|
|
||||||
entry={showModelLoadBalancingEntryModal.payload.entry}
|
|
||||||
onCancel={handleCancelModelLoadBalancingEntryModal}
|
|
||||||
onSave={handleSaveModelLoadBalancingEntryModal}
|
|
||||||
onRemove={handleRemoveModelLoadBalancingEntry}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{showOpeningModal && (
|
{showOpeningModal && (
|
||||||
<OpeningSettingModal
|
<OpeningSettingModal
|
||||||
data={showOpeningModal.payload}
|
data={showOpeningModal.payload}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const translation = {
|
|||||||
deleteApp: 'Delete App',
|
deleteApp: 'Delete App',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
setup: 'Setup',
|
setup: 'Setup',
|
||||||
|
config: 'Config',
|
||||||
getForFree: 'Get for free',
|
getForFree: 'Get for free',
|
||||||
reload: 'Reload',
|
reload: 'Reload',
|
||||||
ok: 'OK',
|
ok: 'OK',
|
||||||
@@ -486,6 +487,18 @@ const translation = {
|
|||||||
discoverMore: 'Discover more in ',
|
discoverMore: 'Discover more in ',
|
||||||
emptyProviderTitle: 'Model provider not set up',
|
emptyProviderTitle: 'Model provider not set up',
|
||||||
emptyProviderTip: 'Please install a model provider first.',
|
emptyProviderTip: 'Please install a model provider first.',
|
||||||
|
auth: {
|
||||||
|
unAuthorized: 'Unauthorized',
|
||||||
|
authRemoved: 'Auth removed',
|
||||||
|
apiKeys: 'API Keys',
|
||||||
|
addApiKey: 'Add API Key',
|
||||||
|
addNewModel: 'Add new model',
|
||||||
|
addCredential: 'Add credential',
|
||||||
|
addModelCredential: 'Add model credential',
|
||||||
|
modelCredentials: 'Model credentials',
|
||||||
|
configModel: 'Config model',
|
||||||
|
configLoadBalancing: 'Config Load Balancing',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
add: 'Add a data source',
|
add: 'Add a data source',
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const translation = {
|
|||||||
deleteApp: '删除应用',
|
deleteApp: '删除应用',
|
||||||
settings: '设置',
|
settings: '设置',
|
||||||
setup: '设置',
|
setup: '设置',
|
||||||
|
config: '配置',
|
||||||
getForFree: '免费获取',
|
getForFree: '免费获取',
|
||||||
reload: '刷新',
|
reload: '刷新',
|
||||||
ok: '好的',
|
ok: '好的',
|
||||||
@@ -486,6 +487,18 @@ const translation = {
|
|||||||
discoverMore: '发现更多就在',
|
discoverMore: '发现更多就在',
|
||||||
emptyProviderTitle: '尚未安装模型供应商',
|
emptyProviderTitle: '尚未安装模型供应商',
|
||||||
emptyProviderTip: '请安装模型供应商。',
|
emptyProviderTip: '请安装模型供应商。',
|
||||||
|
auth: {
|
||||||
|
unAuthorized: '未授权',
|
||||||
|
authRemoved: '授权已移除',
|
||||||
|
apiKeys: 'API 密钥',
|
||||||
|
addApiKey: '添加 API 密钥',
|
||||||
|
addNewModel: '添加新模型',
|
||||||
|
addCredential: '添加凭据',
|
||||||
|
addModelCredential: '添加模型凭据',
|
||||||
|
modelCredentials: '模型凭据',
|
||||||
|
configModel: '配置模型',
|
||||||
|
configLoadBalancing: '配置负载均衡',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
add: '添加数据源',
|
add: '添加数据源',
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
import { get } from './base'
|
import {
|
||||||
|
del,
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
} from './base'
|
||||||
import type {
|
import type {
|
||||||
|
ModelCredential,
|
||||||
ModelItem,
|
ModelItem,
|
||||||
|
ModelLoadBalancingConfig,
|
||||||
|
ModelTypeEnum,
|
||||||
|
ProviderCredential,
|
||||||
} 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 +25,129 @@ 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 useGetProviderCredential = (enabled: boolean, provider: string, credentialId?: string) => {
|
||||||
|
return useQuery({
|
||||||
|
enabled,
|
||||||
|
queryKey: [NAME_SPACE, 'model-list', provider, credentialId],
|
||||||
|
queryFn: () => get<ProviderCredential>(`/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAddProviderCredential = (provider: string) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (data: ProviderCredential) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/credentials`, {
|
||||||
|
body: data,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useEditProviderCredential = (provider: string) => {
|
||||||
|
return useMutation({
|
||||||
|
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<ModelCredential>(`/workspaces/current/model-providers/${provider}/models/credentials?model=${model}&model_type=${modelType}&config_from=${configFrom}${credentialId ? `&credential_id=${credentialId}` : ''}`),
|
||||||
|
staleTime: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUpdateModelLoadBalancingConfig = (provider: string) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (data: {
|
||||||
|
config_from: string
|
||||||
|
model: string
|
||||||
|
model_type: ModelTypeEnum
|
||||||
|
load_balancing: ModelLoadBalancingConfig
|
||||||
|
}) => post<{ result: string }>(`/workspaces/current/model-providers/${provider}/models`, {
|
||||||
|
body: data,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user