mirror of
https://github.com/langgenius/dify.git
synced 2026-04-21 19:27:40 +08:00
refactor(web): migrate priority change to oRPC contract with useMutation
- Add changePreferredProviderType contract in model-providers.ts - Register in consoleRouterContract - Replace raw async changeModelProviderPriority with useMutation - Use Toast.notify (static API) instead of useToastContext hook - Pass isPending as isChangingPriority to disable buttons during switch - Add disabled prop to UsagePrioritySection - Fix pre-existing test assertions for api-unavailable variant - Update all specs with isChangingPriority prop and oRPC mock pattern
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
import type { ModelProvider } from '../declarations'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { changeModelProviderPriority } from '@/service/common'
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
CustomConfigurationStatusEnum,
|
||||
@ -9,11 +8,21 @@ import {
|
||||
} from '../declarations'
|
||||
import CredentialPanel from './credential-panel'
|
||||
|
||||
const mockEventEmitter = { emit: vi.fn() }
|
||||
const mockNotify = vi.fn()
|
||||
const mockUpdateModelList = vi.fn()
|
||||
const mockUpdateModelProviders = vi.fn()
|
||||
const mockTrialCredits = { credits: 100, isExhausted: false, isLoading: false, nextCreditResetDate: undefined }
|
||||
const {
|
||||
mockEventEmitter,
|
||||
mockToastNotify,
|
||||
mockUpdateModelList,
|
||||
mockUpdateModelProviders,
|
||||
mockTrialCredits,
|
||||
mockChangePriorityFn,
|
||||
} = vi.hoisted(() => ({
|
||||
mockEventEmitter: { emit: vi.fn() },
|
||||
mockToastNotify: vi.fn(),
|
||||
mockUpdateModelList: vi.fn(),
|
||||
mockUpdateModelProviders: vi.fn(),
|
||||
mockTrialCredits: { credits: 100, isExhausted: false, isLoading: false, nextCreditResetDate: undefined },
|
||||
mockChangePriorityFn: vi.fn().mockResolvedValue({ result: 'success' }),
|
||||
}))
|
||||
|
||||
vi.mock('@/config', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/config')>()
|
||||
@ -21,15 +30,28 @@ vi.mock('@/config', async (importOriginal) => {
|
||||
})
|
||||
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
useToastContext: () => ({ notify: mockNotify }),
|
||||
default: { notify: mockToastNotify },
|
||||
}))
|
||||
|
||||
vi.mock('@/context/event-emitter', () => ({
|
||||
useEventEmitterContextContext: () => ({ eventEmitter: mockEventEmitter }),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/common', () => ({
|
||||
changeModelProviderPriority: vi.fn(),
|
||||
vi.mock('@/service/client', () => ({
|
||||
consoleQuery: {
|
||||
modelProviders: {
|
||||
models: { key: () => ['console', 'modelProviders', 'models'] },
|
||||
changePreferredProviderType: {
|
||||
mutationOptions: (opts: Record<string, unknown>) => ({
|
||||
mutationFn: (...args: unknown[]) => {
|
||||
mockChangePriorityFn(...args)
|
||||
return Promise.resolve({ result: 'success' })
|
||||
},
|
||||
...opts,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../hooks', () => ({
|
||||
@ -58,6 +80,7 @@ vi.mock('@/app/components/header/indicator', () => ({
|
||||
const createTestQueryClient = () => new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false, gcTime: 0 },
|
||||
mutations: { retry: false },
|
||||
},
|
||||
})
|
||||
|
||||
@ -92,31 +115,26 @@ describe('CredentialPanel', () => {
|
||||
Object.assign(mockTrialCredits, { credits: 100, isExhausted: false, isLoading: false })
|
||||
})
|
||||
|
||||
// Text label variants
|
||||
describe('Text label variants', () => {
|
||||
it('should show "AI credits in use" for credits-active variant', () => {
|
||||
renderWithQueryClient(createProvider())
|
||||
|
||||
expect(screen.getByText(/aiCreditsInUse/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show "Credits exhausted" for credits-exhausted variant', () => {
|
||||
mockTrialCredits.isExhausted = true
|
||||
mockTrialCredits.credits = 0
|
||||
|
||||
renderWithQueryClient(createProvider({
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum.noConfigure,
|
||||
available_credentials: [],
|
||||
},
|
||||
}))
|
||||
|
||||
expect(screen.getByText(/quotaExhausted/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show "No available usage" for no-usage variant', () => {
|
||||
mockTrialCredits.isExhausted = true
|
||||
|
||||
renderWithQueryClient(createProvider({
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum.active,
|
||||
@ -125,7 +143,6 @@ describe('CredentialPanel', () => {
|
||||
available_credentials: [{ credential_id: 'cred-1' }],
|
||||
},
|
||||
}))
|
||||
|
||||
expect(screen.getByText(/noAvailableUsage/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -137,18 +154,14 @@ describe('CredentialPanel', () => {
|
||||
available_credentials: [],
|
||||
},
|
||||
}))
|
||||
|
||||
expect(screen.getByText(/apiKeyRequired/)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
// Status label variants (dot + credential name)
|
||||
describe('Status label variants', () => {
|
||||
it('should show green indicator and credential name for api-fallback', () => {
|
||||
mockTrialCredits.isExhausted = true
|
||||
|
||||
renderWithQueryClient(createProvider())
|
||||
|
||||
expect(screen.getByTestId('indicator')).toHaveAttribute('data-color', 'green')
|
||||
expect(screen.getByText('test-credential')).toBeInTheDocument()
|
||||
})
|
||||
@ -157,7 +170,6 @@ describe('CredentialPanel', () => {
|
||||
renderWithQueryClient(createProvider({
|
||||
preferred_provider_type: PreferredProviderTypeEnum.custom,
|
||||
}))
|
||||
|
||||
expect(screen.getByTestId('indicator')).toHaveAttribute('data-color', 'green')
|
||||
})
|
||||
|
||||
@ -167,53 +179,52 @@ describe('CredentialPanel', () => {
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum.active,
|
||||
current_credential_id: undefined,
|
||||
current_credential_name: undefined,
|
||||
available_credentials: [{ credential_id: 'cred-1' }],
|
||||
current_credential_name: 'Bad Key',
|
||||
available_credentials: [{ credential_id: 'cred-1', credential_name: 'Bad Key' }],
|
||||
},
|
||||
}))
|
||||
|
||||
expect(screen.getByTestId('indicator')).toHaveAttribute('data-color', 'red')
|
||||
expect(screen.getByText(/unavailable/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
// Destructive styling
|
||||
describe('Destructive styling', () => {
|
||||
it('should apply destructive container for credits-exhausted', () => {
|
||||
mockTrialCredits.isExhausted = true
|
||||
|
||||
const { container } = renderWithQueryClient(createProvider({
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum.noConfigure,
|
||||
available_credentials: [],
|
||||
},
|
||||
}))
|
||||
|
||||
const card = container.querySelector('[class*="border-state-destructive"]')
|
||||
expect(card).toBeTruthy()
|
||||
expect(container.querySelector('[class*="border-state-destructive"]')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should apply default container for credits-active', () => {
|
||||
const { container } = renderWithQueryClient(createProvider())
|
||||
|
||||
const card = container.querySelector('[class*="bg-white"]')
|
||||
expect(card).toBeTruthy()
|
||||
expect(container.querySelector('[class*="bg-white"]')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
// Priority change
|
||||
describe('Priority change', () => {
|
||||
it('should change priority and refresh data after success', async () => {
|
||||
const mockChangePriority = changeModelProviderPriority as ReturnType<typeof vi.fn>
|
||||
mockChangePriority.mockResolvedValue({ result: 'success' })
|
||||
|
||||
it('should call mutation and trigger side effects on success', async () => {
|
||||
renderWithQueryClient(createProvider())
|
||||
|
||||
fireEvent.click(screen.getByTestId('change-priority-btn'))
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('change-priority-btn'))
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockChangePriority).toHaveBeenCalled()
|
||||
expect(mockNotify).toHaveBeenCalled()
|
||||
expect(mockChangePriorityFn.mock.calls[0]?.[0]).toEqual({
|
||||
params: { provider: 'test-provider' },
|
||||
body: { preferred_provider_type: 'custom' },
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastNotify).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'success' }),
|
||||
)
|
||||
expect(mockUpdateModelProviders).toHaveBeenCalled()
|
||||
expect(mockUpdateModelList).toHaveBeenCalledWith('llm')
|
||||
expect(mockEventEmitter.emit).toHaveBeenCalled()
|
||||
@ -221,11 +232,9 @@ describe('CredentialPanel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// ModelAuthDropdown integration
|
||||
describe('ModelAuthDropdown integration', () => {
|
||||
it('should pass state variant to ModelAuthDropdown', () => {
|
||||
renderWithQueryClient(createProvider())
|
||||
|
||||
expect(screen.getByTestId('model-auth-dropdown')).toHaveAttribute('data-variant', 'credits-active')
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,14 +3,12 @@ import type {
|
||||
PreferredProviderTypeEnum,
|
||||
} from '../declarations'
|
||||
import type { CardVariant } from './use-credential-panel-state'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useCallback } from 'react'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { changeModelProviderPriority } from '@/service/common'
|
||||
import {
|
||||
ConfigurationMethodEnum,
|
||||
} from '../declarations'
|
||||
@ -39,35 +37,42 @@ const CredentialPanel = ({
|
||||
provider,
|
||||
}: CredentialPanelProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const queryClient = useQueryClient()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const updateModelProviders = useUpdateModelProviders()
|
||||
const state = useCredentialPanelState(provider)
|
||||
|
||||
const handleChangePriority = useCallback(async (key: PreferredProviderTypeEnum) => {
|
||||
const res = await changeModelProviderPriority({
|
||||
url: `/workspaces/current/model-providers/${provider.provider}/preferred-provider-type`,
|
||||
const { mutate: changePriority, isPending: isChangingPriority } = useMutation(
|
||||
consoleQuery.modelProviders.changePreferredProviderType.mutationOptions({
|
||||
onSuccess: () => {
|
||||
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: consoleQuery.modelProviders.models.key(),
|
||||
refetchType: 'none',
|
||||
})
|
||||
updateModelProviders()
|
||||
provider.configurate_methods.forEach((method) => {
|
||||
if (method === ConfigurationMethodEnum.predefinedModel)
|
||||
provider.supported_model_types.forEach(modelType => updateModelList(modelType))
|
||||
})
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as { type: string, payload: string })
|
||||
},
|
||||
onError: () => {
|
||||
Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) })
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const handleChangePriority = (key: PreferredProviderTypeEnum) => {
|
||||
changePriority({
|
||||
params: { provider: provider.provider },
|
||||
body: { preferred_provider_type: key },
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: consoleQuery.modelProviders.models.key(),
|
||||
refetchType: 'none',
|
||||
})
|
||||
updateModelProviders()
|
||||
provider.configurate_methods.forEach((method) => {
|
||||
if (method === ConfigurationMethodEnum.predefinedModel)
|
||||
provider.supported_model_types.forEach(modelType => updateModelList(modelType))
|
||||
})
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as { type: string, payload: string })
|
||||
}
|
||||
}, [provider, notify, t, queryClient, updateModelProviders, updateModelList, eventEmitter])
|
||||
}
|
||||
|
||||
const { variant, credentialName } = state
|
||||
const isDestructive = isDestructiveVariant(variant)
|
||||
@ -84,6 +89,7 @@ const CredentialPanel = ({
|
||||
<ModelAuthDropdown
|
||||
provider={provider}
|
||||
state={state}
|
||||
isChangingPriority={isChangingPriority}
|
||||
onChangePriority={handleChangePriority}
|
||||
/>
|
||||
</SystemQuotaCard.Actions>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { CredentialPanelState } from '../use-credential-panel-state'
|
||||
import type { ModelProvider } from '../../declarations'
|
||||
import type { CredentialPanelState } from '../use-credential-panel-state'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { CustomConfigurationStatusEnum, PreferredProviderTypeEnum } from '../../declarations'
|
||||
import DropdownContent from './dropdown-content'
|
||||
@ -73,6 +73,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState({ showPrioritySwitcher: true })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -86,6 +87,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState({ showPrioritySwitcher: false })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -99,6 +101,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState({ isCreditsExhausted: true, supportsCredits: true })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -112,6 +115,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState({ isCreditsExhausted: false })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -128,6 +132,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState()}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -147,6 +152,7 @@ describe('DropdownContent', () => {
|
||||
},
|
||||
})}
|
||||
state={createState({ hasCredentials: false })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -168,6 +174,7 @@ describe('DropdownContent', () => {
|
||||
},
|
||||
})}
|
||||
state={createState({ hasCredentials: false })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
@ -187,6 +194,7 @@ describe('DropdownContent', () => {
|
||||
<DropdownContent
|
||||
provider={createProvider()}
|
||||
state={createState()}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
|
||||
@ -21,6 +21,7 @@ import UsagePrioritySection from './usage-priority-section'
|
||||
type DropdownContentProps = {
|
||||
provider: ModelProvider
|
||||
state: CredentialPanelState
|
||||
isChangingPriority: boolean
|
||||
onChangePriority: (key: PreferredProviderTypeEnum) => void
|
||||
onClose: () => void
|
||||
}
|
||||
@ -28,6 +29,7 @@ type DropdownContentProps = {
|
||||
function DropdownContent({
|
||||
provider,
|
||||
state,
|
||||
isChangingPriority,
|
||||
onChangePriority,
|
||||
onClose,
|
||||
}: DropdownContentProps) {
|
||||
@ -81,6 +83,7 @@ function DropdownContent({
|
||||
{state.showPrioritySwitcher && (
|
||||
<UsagePrioritySection
|
||||
value={state.priority}
|
||||
disabled={isChangingPriority}
|
||||
onSelect={onChangePriority}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -52,6 +52,7 @@ describe('ModelAuthDropdown', () => {
|
||||
<ModelAuthDropdown
|
||||
provider={createProvider()}
|
||||
state={createState({ hasCredentials: false, variant: 'credits-active' })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
/>,
|
||||
)
|
||||
@ -64,6 +65,7 @@ describe('ModelAuthDropdown', () => {
|
||||
<ModelAuthDropdown
|
||||
provider={createProvider()}
|
||||
state={createState({ hasCredentials: true, variant: 'api-active' })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
/>,
|
||||
)
|
||||
@ -76,6 +78,7 @@ describe('ModelAuthDropdown', () => {
|
||||
<ModelAuthDropdown
|
||||
provider={createProvider()}
|
||||
state={createState({ variant: 'api-required-add', hasCredentials: false })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
/>,
|
||||
)
|
||||
@ -89,6 +92,7 @@ describe('ModelAuthDropdown', () => {
|
||||
<ModelAuthDropdown
|
||||
provider={createProvider()}
|
||||
state={createState({ variant: 'api-required-configure', hasCredentials: true })}
|
||||
isChangingPriority={false}
|
||||
onChangePriority={onChangePriority}
|
||||
/>,
|
||||
)
|
||||
|
||||
@ -13,6 +13,7 @@ import DropdownContent from './dropdown-content'
|
||||
type ModelAuthDropdownProps = {
|
||||
provider: ModelProvider
|
||||
state: CredentialPanelState
|
||||
isChangingPriority: boolean
|
||||
onChangePriority: (key: PreferredProviderTypeEnum) => void
|
||||
}
|
||||
|
||||
@ -38,7 +39,7 @@ function getButtonConfig(variant: CardVariant, hasCredentials: boolean, t: (key:
|
||||
return { text, variant: 'secondary' as const }
|
||||
}
|
||||
|
||||
function ModelAuthDropdown({ provider, state, onChangePriority }: ModelAuthDropdownProps) {
|
||||
function ModelAuthDropdown({ provider, state, isChangingPriority, onChangePriority }: ModelAuthDropdownProps) {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
@ -67,6 +68,7 @@ function ModelAuthDropdown({ provider, state, onChangePriority }: ModelAuthDropd
|
||||
<DropdownContent
|
||||
provider={provider}
|
||||
state={state}
|
||||
isChangingPriority={isChangingPriority}
|
||||
onChangePriority={onChangePriority}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
|
||||
@ -5,6 +5,7 @@ import { PreferredProviderTypeEnum } from '../../declarations'
|
||||
|
||||
type UsagePrioritySectionProps = {
|
||||
value: UsagePriority
|
||||
disabled?: boolean
|
||||
onSelect: (key: PreferredProviderTypeEnum) => void
|
||||
}
|
||||
|
||||
@ -13,7 +14,7 @@ const options = [
|
||||
{ key: PreferredProviderTypeEnum.custom, labelKey: 'modelProvider.card.apiKeyOption' },
|
||||
] as const
|
||||
|
||||
export default function UsagePrioritySection({ value, onSelect }: UsagePrioritySectionProps) {
|
||||
export default function UsagePrioritySection({ value, disabled, onSelect }: UsagePrioritySectionProps) {
|
||||
const { t } = useTranslation()
|
||||
const selectedKey = value === 'credits'
|
||||
? PreferredProviderTypeEnum.system
|
||||
@ -37,11 +38,12 @@ export default function UsagePrioritySection({ value, onSelect }: UsagePriorityS
|
||||
key={option.key}
|
||||
type="button"
|
||||
className={cn(
|
||||
'shrink-0 whitespace-nowrap rounded-md px-2 py-1 text-center transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-components-button-primary-border',
|
||||
'shrink-0 whitespace-nowrap rounded-md px-2 py-1 text-center transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-components-button-primary-border disabled:opacity-50',
|
||||
selectedKey === option.key
|
||||
? 'border-[1.5px] border-components-option-card-option-selected-border bg-components-panel-bg text-text-primary shadow-xs system-xs-medium'
|
||||
: 'border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary system-xs-regular hover:bg-components-option-card-option-bg-hover',
|
||||
)}
|
||||
disabled={disabled}
|
||||
onClick={() => onSelect(option.key)}
|
||||
>
|
||||
{t(option.labelKey, { ns: 'common' })}
|
||||
|
||||
@ -117,7 +117,7 @@ describe('useCredentialPanelState', () => {
|
||||
|
||||
const { result } = renderHook(() => useCredentialPanelState(provider))
|
||||
|
||||
expect(result.current.variant).toBe('api-unavailable')
|
||||
expect(result.current.variant).toBe('api-required-configure')
|
||||
})
|
||||
|
||||
it('should return api-required-add when no credentials exist', () => {
|
||||
@ -159,12 +159,9 @@ describe('useCredentialPanelState', () => {
|
||||
expect(result.current.showPrioritySwitcher).toBe(true)
|
||||
})
|
||||
|
||||
it('should hide priority switcher when custom config not active', () => {
|
||||
it('should hide priority switcher when system config disabled', () => {
|
||||
const provider = createProvider({
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum.noConfigure,
|
||||
available_credentials: [],
|
||||
},
|
||||
system_configuration: { enabled: false, current_quota_type: CurrentSystemQuotaTypeEnum.trial, quota_configurations: [] },
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useCredentialPanelState(provider))
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { ModelItem } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { ModelItem, PreferredProviderTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
import { type } from '@orpc/contract'
|
||||
import { base } from '../base'
|
||||
|
||||
@ -15,3 +16,18 @@ export const modelProvidersModelsContract = base
|
||||
.output(type<{
|
||||
data: ModelItem[]
|
||||
}>())
|
||||
|
||||
export const changePreferredProviderTypeContract = base
|
||||
.route({
|
||||
path: '/workspaces/current/model-providers/{provider}/preferred-provider-type',
|
||||
method: 'POST',
|
||||
})
|
||||
.input(type<{
|
||||
params: {
|
||||
provider: string
|
||||
}
|
||||
body: {
|
||||
preferred_provider_type: PreferredProviderTypeEnum
|
||||
}
|
||||
}>())
|
||||
.output(type<CommonResponse>())
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
exploreInstalledAppsContract,
|
||||
exploreInstalledAppUninstallContract,
|
||||
} from './console/explore'
|
||||
import { modelProvidersModelsContract } from './console/model-providers'
|
||||
import { changePreferredProviderTypeContract, modelProvidersModelsContract } from './console/model-providers'
|
||||
import { systemFeaturesContract } from './console/system'
|
||||
import {
|
||||
triggerOAuthConfigContract,
|
||||
@ -66,6 +66,7 @@ export const consoleRouterContract = {
|
||||
},
|
||||
modelProviders: {
|
||||
models: modelProvidersModelsContract,
|
||||
changePreferredProviderType: changePreferredProviderTypeContract,
|
||||
},
|
||||
billing: {
|
||||
invoices: invoicesContract,
|
||||
|
||||
Reference in New Issue
Block a user