From 594c21d98c9e43dc7b130efe95fdc8335f7c7fde Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Sun, 12 Jul 2026 22:24:27 +0800
Subject: [PATCH] fix(web): split model selection and settings actions (#38797)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
---
eslint-suppressions.json | 4 +-
.../__tests__/derive-trigger-status.spec.ts | 112 ------
.../__tests__/index.spec.tsx | 57 +++-
.../__tests__/trigger.spec.tsx | 322 ------------------
.../derive-trigger-status.ts | 9 -
.../model-parameter-modal/index.tsx | 87 +++--
.../model-parameter-modal/trigger.tsx | 138 --------
.../model-parameter-modal/types.ts | 13 +
.../model-selector/__tests__/index.spec.tsx | 184 +++++-----
.../model-selector/index.tsx | 105 +++---
10 files changed, 273 insertions(+), 758 deletions(-)
delete mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/derive-trigger-status.spec.ts
delete mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/trigger.spec.tsx
delete mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/derive-trigger-status.ts
delete mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx
create mode 100644 web/app/components/header/account-setting/model-provider-page/model-parameter-modal/types.ts
diff --git a/eslint-suppressions.json b/eslint-suppressions.json
index 27a40b17491..d767bfc4301 100644
--- a/eslint-suppressions.json
+++ b/eslint-suppressions.json
@@ -3704,10 +3704,10 @@
},
"web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx": {
"jsx-a11y/click-events-have-key-events": {
- "count": 3
+ "count": 2
},
"jsx-a11y/no-static-element-interactions": {
- "count": 3
+ "count": 2
}
},
"web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx": {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/derive-trigger-status.spec.ts b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/derive-trigger-status.spec.ts
deleted file mode 100644
index 3186199524c..00000000000
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/derive-trigger-status.spec.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import type { ModelItem, ModelProvider } from '../../declarations'
-import type { CredentialPanelState } from '../../provider-added-card/use-credential-panel-state'
-import { ModelStatusEnum } from '../../declarations'
-import { deriveTriggerStatus } from '../derive-trigger-status'
-
-const baseCredentialState: CredentialPanelState = {
- variant: 'api-active',
- priority: 'apiKey',
- supportsCredits: true,
- showPrioritySwitcher: true,
- hasCredentials: true,
- isCreditsExhausted: false,
- credentialName: 'Primary Key',
- credits: 10,
-}
-
-const mockProvider = { provider: 'openai' } as ModelProvider
-const mockModel = { model: 'gpt-4', status: ModelStatusEnum.active } as ModelItem
-
-describe('deriveTriggerStatus', () => {
- it('returns empty when modelId is missing', () => {
- expect(deriveTriggerStatus(undefined, 'openai', mockProvider, mockModel, baseCredentialState)).toBe('empty')
- })
-
- it('returns empty when providerName is missing', () => {
- expect(deriveTriggerStatus('gpt-4', undefined, mockProvider, mockModel, baseCredentialState)).toBe('empty')
- })
-
- it('returns incompatible when provider plugin is not installed', () => {
- expect(deriveTriggerStatus('gpt-4', 'openai', undefined, mockModel, baseCredentialState)).toBe('incompatible')
- })
-
- it('returns credits-exhausted when credits priority and exhausted', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- priority: 'credits',
- isCreditsExhausted: true,
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, state)).toBe('credits-exhausted')
- })
-
- it('returns active when credits priority but not exhausted', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- priority: 'credits',
- isCreditsExhausted: false,
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, state)).toBe('active')
- })
-
- it('returns api-key-unavailable when variant is api-unavailable', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- variant: 'api-unavailable',
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, state)).toBe('api-key-unavailable')
- })
-
- it('returns incompatible when currentModel is missing (deprecated)', () => {
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, undefined, baseCredentialState)).toBe('incompatible')
- })
-
- it('returns credits-exhausted when currentModel is missing and AI credits are exhausted without api key', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- priority: 'apiKey',
- hasCredentials: false,
- isCreditsExhausted: true,
- credentialName: undefined,
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, undefined, state)).toBe('credits-exhausted')
- })
-
- it('returns configure-required when model status is no-configure', () => {
- const model = { ...mockModel, status: ModelStatusEnum.noConfigure } as ModelItem
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, model, baseCredentialState)).toBe('configure-required')
- })
-
- it('returns incompatible when model status is noPermission', () => {
- const model = { ...mockModel, status: ModelStatusEnum.noPermission } as ModelItem
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, model, baseCredentialState)).toBe('incompatible')
- })
-
- it('returns disabled when model status is disabled', () => {
- const model = { ...mockModel, status: ModelStatusEnum.disabled } as ModelItem
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, model, baseCredentialState)).toBe('disabled')
- })
-
- it('returns active when all conditions are satisfied', () => {
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, baseCredentialState)).toBe('active')
- })
-
- it('prioritises credits-exhausted over api-unavailable', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- priority: 'credits',
- isCreditsExhausted: true,
- variant: 'api-unavailable',
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, state)).toBe('credits-exhausted')
- })
-
- it('does not return credits-exhausted when supportsCredits is false', () => {
- const state: CredentialPanelState = {
- ...baseCredentialState,
- priority: 'credits',
- isCreditsExhausted: true,
- supportsCredits: false,
- }
- expect(deriveTriggerStatus('gpt-4', 'openai', mockProvider, mockModel, state)).toBe('active')
- })
-})
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/index.spec.tsx
index b3f1ee9be13..fbb5e9336af 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/index.spec.tsx
@@ -123,6 +123,7 @@ vi.mock('@/config', async (importOriginal) => {
})
describe('ModelParameterModal', () => {
+ const openSettings = () => fireEvent.click(screen.getByRole('button', { name: /modelProvider\.modelSettings/i }))
const defaultProps = {
isAdvancedMode: false,
modelId: 'gpt-3.5-turbo',
@@ -179,14 +180,42 @@ describe('ModelParameterModal', () => {
it('should render trigger and open modal content when trigger is clicked', () => {
render(