mirror of
https://github.com/langgenius/dify.git
synced 2026-03-30 18:40:17 +08:00
fix(provider): handle undefined provider in credential status and panel state
This commit is contained in:
@ -53,4 +53,14 @@ describe('useCredentialStatus', () => {
|
||||
expect(result.current.hasCredential).toBe(false)
|
||||
expect(result.current.available_credentials).toBeUndefined()
|
||||
})
|
||||
|
||||
it('handles undefined provider gracefully', () => {
|
||||
const { result } = renderHook(() => useCredentialStatus(undefined))
|
||||
expect(result.current.hasCredential).toBe(false)
|
||||
expect(result.current.authorized).toBeFalsy()
|
||||
expect(result.current.authRemoved).toBe(false)
|
||||
expect(result.current.available_credentials).toBeUndefined()
|
||||
expect(result.current.current_credential_id).toBeUndefined()
|
||||
expect(result.current.current_credential_name).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,12 +3,12 @@ import type {
|
||||
} from '../../declarations'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
export const useCredentialStatus = (provider: ModelProvider) => {
|
||||
export const useCredentialStatus = (provider: ModelProvider | undefined) => {
|
||||
const {
|
||||
current_credential_id,
|
||||
current_credential_name,
|
||||
available_credentials,
|
||||
} = provider.custom_configuration
|
||||
} = 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
|
||||
|
||||
@ -128,6 +128,18 @@ describe('PopupItem', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should render nothing when provider is not found in modelProviders', () => {
|
||||
mockUseProviderContext.mockReturnValue({
|
||||
modelProviders: [],
|
||||
})
|
||||
|
||||
const { container } = render(
|
||||
<PopupItem model={makeModel()} onSelect={vi.fn()} onHide={vi.fn()} />,
|
||||
)
|
||||
|
||||
expect(container.innerHTML).toBe('')
|
||||
})
|
||||
|
||||
it('should call onSelect when clicking an active model', () => {
|
||||
const onSelect = vi.fn()
|
||||
render(<PopupItem model={makeModel()} onSelect={onSelect} onHide={vi.fn()} />)
|
||||
|
||||
@ -59,7 +59,7 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
const { modelProviders } = useProviderContext()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const updateModelProviders = useUpdateModelProviders()
|
||||
const currentProvider = modelProviders.find(provider => provider.provider === model.provider)!
|
||||
const currentProvider = modelProviders.find(provider => provider.provider === model.provider)
|
||||
const handleSelect = (provider: string, modelItem: ModelItem) => {
|
||||
if (modelItem.status !== ModelStatusEnum.active)
|
||||
return
|
||||
@ -67,6 +67,8 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
onSelect(provider, modelItem)
|
||||
}
|
||||
const handleOpenModelModal = () => {
|
||||
if (!currentProvider)
|
||||
return
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider,
|
||||
@ -96,6 +98,9 @@ const PopupItem: FC<PopupItemProps> = ({
|
||||
onHide()
|
||||
}, [onHide])
|
||||
|
||||
if (!currentProvider)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className="mb-1">
|
||||
<div className="flex h-[22px] items-center justify-between px-3 text-xs font-medium text-text-tertiary">
|
||||
|
||||
@ -137,11 +137,11 @@ const Popup: FC<PopupProps> = ({
|
||||
}).filter(model => model.models.length > 0)
|
||||
|
||||
if (defaultModel?.provider) {
|
||||
const selectedIndex = filtered.findIndex(m => m.provider === defaultModel.provider)
|
||||
if (selectedIndex > 0) {
|
||||
const [selected] = filtered.splice(selectedIndex, 1)
|
||||
filtered.unshift(selected)
|
||||
}
|
||||
filtered.sort((a, b) => {
|
||||
const aSelected = a.provider === defaultModel.provider ? 0 : 1
|
||||
const bSelected = b.provider === defaultModel.provider ? 0 : 1
|
||||
return aSelected - bSelected
|
||||
})
|
||||
}
|
||||
|
||||
return filtered
|
||||
|
||||
@ -6,12 +6,12 @@ import { consoleQuery } from '@/service/client'
|
||||
import { ConfigurationMethodEnum } from '../declarations'
|
||||
import { useUpdateModelList, useUpdateModelProviders } from '../hooks'
|
||||
|
||||
export function useChangeProviderPriority(provider: ModelProvider) {
|
||||
export function useChangeProviderPriority(provider: ModelProvider | undefined) {
|
||||
const { t } = useTranslation()
|
||||
const queryClient = useQueryClient()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const updateModelProviders = useUpdateModelProviders()
|
||||
const providerName = provider.provider
|
||||
const providerName = provider?.provider ?? ''
|
||||
|
||||
const modelProviderModelListQueryKey = consoleQuery.modelProviders.models.queryKey({
|
||||
input: {
|
||||
@ -31,9 +31,9 @@ export function useChangeProviderPriority(provider: ModelProvider) {
|
||||
refetchType: 'none',
|
||||
})
|
||||
updateModelProviders()
|
||||
provider.configurate_methods.forEach((method) => {
|
||||
provider?.configurate_methods.forEach((method) => {
|
||||
if (method === ConfigurationMethodEnum.predefinedModel)
|
||||
provider.supported_model_types.forEach(modelType => updateModelList(modelType))
|
||||
provider?.supported_model_types.forEach(modelType => updateModelList(modelType))
|
||||
})
|
||||
},
|
||||
onError: () => {
|
||||
|
||||
@ -183,6 +183,18 @@ describe('useCredentialPanelState', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// Undefined provider
|
||||
describe('Undefined provider', () => {
|
||||
it('should return safe defaults when provider is undefined', () => {
|
||||
const { result } = renderHook(() => useCredentialPanelState(undefined))
|
||||
|
||||
expect(result.current.priority).toBe('apiKeyOnly')
|
||||
expect(result.current.supportsCredits).toBe(false)
|
||||
expect(result.current.hasCredentials).toBe(false)
|
||||
expect(result.current.credentialName).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
// Derived metadata
|
||||
describe('Derived metadata', () => {
|
||||
it('should show priority switcher when credits supported and custom config active', () => {
|
||||
|
||||
@ -70,7 +70,7 @@ function deriveVariant(
|
||||
return 'api-required-add'
|
||||
}
|
||||
|
||||
export function useCredentialPanelState(provider: ModelProvider): CredentialPanelState {
|
||||
export function useCredentialPanelState(provider: ModelProvider | undefined): CredentialPanelState {
|
||||
const { isExhausted, credits } = useTrialCredits()
|
||||
const {
|
||||
hasCredential,
|
||||
@ -78,10 +78,10 @@ export function useCredentialPanelState(provider: ModelProvider): CredentialPane
|
||||
current_credential_name,
|
||||
} = useCredentialStatus(provider)
|
||||
|
||||
const systemConfig = provider.system_configuration
|
||||
const preferredType = provider.preferred_provider_type
|
||||
const systemConfig = provider?.system_configuration
|
||||
const preferredType = provider?.preferred_provider_type
|
||||
|
||||
const supportsCredits = systemConfig.enabled && IS_CLOUD_EDITION
|
||||
const supportsCredits = !!systemConfig?.enabled && IS_CLOUD_EDITION
|
||||
|
||||
const priority: UsagePriority = !supportsCredits
|
||||
? 'apiKeyOnly'
|
||||
|
||||
Reference in New Issue
Block a user