From c41855da811d1c1cee40abbd11b69d9a04d8bc5f Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Wed, 3 Jun 2026 11:59:57 +0800 Subject: [PATCH] Fix: Model provider add verify and fixed form in modal not resetting issue (#15520) ### What problem does this PR solve? Fix: Model provider add verify and fixed form in modal not resetting issue ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/hooks/use-llm-request.tsx | 22 +++++++ web/src/interfaces/request/llm.ts | 1 + .../user-setting/setting-model/hooks.tsx | 63 ++++++++++++++----- .../modal/api-key-modal/index.tsx | 4 +- .../modal/azure-openai-modal/index.tsx | 8 ++- .../modal/bedrock-modal/index.tsx | 8 ++- .../modal/fish-audio-modal/index.tsx | 11 +++- .../modal/google-modal/index.tsx | 12 +++- .../modal/mineru-modal/index.tsx | 8 ++- .../modal/next-tencent-modal/index.tsx | 11 +++- .../modal/opendataloader-modal/index.tsx | 8 ++- .../modal/paddleocr-modal/index.tsx | 8 ++- .../setting-model/modal/spark-modal/index.tsx | 8 ++- .../modal/volcengine-modal/index.tsx | 8 ++- web/src/services/llm-service.ts | 5 ++ web/src/utils/api.ts | 2 + 16 files changed, 160 insertions(+), 27 deletions(-) diff --git a/web/src/hooks/use-llm-request.tsx b/web/src/hooks/use-llm-request.tsx index 5cf69deb60..af17fbfc84 100644 --- a/web/src/hooks/use-llm-request.tsx +++ b/web/src/hooks/use-llm-request.tsx @@ -32,6 +32,7 @@ export const enum LLMApiAction { AddedProviders = 'addedProviders', AddProvider = 'addProvider', AddProviderInstance = 'addProviderInstance', + VerifyProviderConnection = 'verifyProviderConnection', AddInstanceModel = 'addInstanceModel', DeleteProviderInstance = 'deleteProviderInstance', ListDefaultModels = 'listDefaultModels', @@ -238,6 +239,27 @@ export const useAddProviderInstance = () => { return { data, loading, addProviderInstance: mutateAsync }; }; +export const useVerifyProviderConnection = () => { + const { + data, + isPending: loading, + mutateAsync, + } = useMutation({ + mutationKey: [LLMApiAction.VerifyProviderConnection], + mutationFn: async (params: { + provider_name: string; + api_key: string; + base_url?: string; + region?: string; + }) => { + const { data } = await llmService.verifyProviderConnection(params); + return data; + }, + }); + + return { data, loading, verifyProviderConnection: mutateAsync }; +}; + export const useAddInstanceModel = () => { const queryClient = useQueryClient(); const { diff --git a/web/src/interfaces/request/llm.ts b/web/src/interfaces/request/llm.ts index d461b3e3c9..dda7a45f79 100644 --- a/web/src/interfaces/request/llm.ts +++ b/web/src/interfaces/request/llm.ts @@ -24,6 +24,7 @@ export interface IAddProviderRequestBody { export type IAddProviderInstanceRequestBody = IAddLlmRequestBody & { instance_name: string; + region?: string; }; export interface IDeleteProviderInstanceRequestBody { diff --git a/web/src/pages/user-setting/setting-model/hooks.tsx b/web/src/pages/user-setting/setting-model/hooks.tsx index 81f08473f1..07dfc1737a 100644 --- a/web/src/pages/user-setting/setting-model/hooks.tsx +++ b/web/src/pages/user-setting/setting-model/hooks.tsx @@ -5,6 +5,7 @@ import { useAddProviderInstance, useFetchAddedProviders, useFetchProviderInstances, + useVerifyProviderConnection, } from '@/hooks/use-llm-request'; import { IAddProviderInstanceRequestBody } from '@/interfaces/request/llm'; import { getRealModelName } from '@/utils/llm-util'; @@ -87,12 +88,46 @@ export const useHideWhenInstanceExists = (instanceNameSet: Set) => { [instanceNameSet], ); }; +export const useVerifyConnection = () => { + const { verifyProviderConnection } = useVerifyProviderConnection(); + + return useCallback( + async ( + providerName: string, + apiKey: string, + baseUrl?: string, + region?: string, + ) => { + const ret = await verifyProviderConnection({ + provider_name: providerName, + api_key: apiKey, + base_url: baseUrl, + region: region, + }); + + if (ret.code === 0) { + return { + isValid: true, + logs: ret.message, + } as VerifyResult; + } else { + return { + isValid: false, + logs: ret.message, + } as VerifyResult; + } + }, + [verifyProviderConnection], + ); +}; + export const useSubmitApiKey = () => { const [savingParams, setSavingParams] = useState( {} as SavingParamsState, ); const [editMode, setEditMode] = useState(false); const submitProviderInstance = useSubmitProviderInstance(); + const verifyConnection = useVerifyConnection(); const [saveLoading, setSaveLoading] = useState(false); const { visible: apiKeyVisible, @@ -126,6 +161,17 @@ export const useSubmitApiKey = () => { } } + // Use dedicated verify API for verification + if (isVerify) { + const res = await verifyConnection( + savingParams.llm_factory, + postBody.api_key, + postBody.base_url, + region, + ); + return res; + } + const req: IAddProviderInstanceRequestBody = { instance_name: postBody.instance_name || savingParams.instance_name || '', @@ -146,23 +192,8 @@ export const useSubmitApiKey = () => { setEditMode(false); } } - if (isVerify) { - let res = {} as VerifyResult; - if (ret.data?.success) { - res = { - isValid: true, - logs: ret.data?.message, - }; - } else { - res = { - isValid: false, - logs: ret.data.message, - }; - } - return res; - } }, - [hideApiKeyModal, submitProviderInstance, savingParams], + [hideApiKeyModal, submitProviderInstance, savingParams, verifyConnection], ); const onShowApiKeyModal = useCallback( diff --git a/web/src/pages/user-setting/setting-model/modal/api-key-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/api-key-modal/index.tsx index af92c74f9c..09b766d60b 100644 --- a/web/src/pages/user-setting/setting-model/modal/api-key-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/api-key-modal/index.tsx @@ -53,7 +53,7 @@ const ApiKeyModal = ({ const { t } = useTranslate('setting'); const handleOk = useCallback(async () => { - await form.handleSubmit((values) => onOk(values))(); + await form.handleSubmit((values) => onOk(values as ApiKeyPostBody))(); }, [form, onOk]); const handleKeyDown: KeyboardEventHandler = useCallback( @@ -68,6 +68,8 @@ const ApiKeyModal = ({ useEffect(() => { if (visible) { form.setValue('api_key', initialValue); + } else { + form.reset(); } }, [initialValue, form, visible]); diff --git a/web/src/pages/user-setting/setting-model/modal/azure-openai-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/azure-openai-modal/index.tsx index 3ed6e54373..fd4500024b 100644 --- a/web/src/pages/user-setting/setting-model/modal/azure-openai-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/azure-openai-modal/index.tsx @@ -14,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -168,6 +168,12 @@ const AzureOpenAIModal = ({ [verifyParamsFunc, onVerify], ); + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } diff --git a/web/src/pages/user-setting/setting-model/modal/bedrock-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/bedrock-modal/index.tsx index 7103103878..e2a405bfaf 100644 --- a/web/src/pages/user-setting/setting-model/modal/bedrock-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/bedrock-modal/index.tsx @@ -15,7 +15,7 @@ import { VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useCallback, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { z } from 'zod'; import { LLMHeader } from '../../components/llm-header'; @@ -213,6 +213,12 @@ const BedrockModal = ({ [verifyParamsFunc, onVerify], ); + useEffect(() => { + if (!visible) { + form.reset(); + } + }, [visible, form]); + return ( } diff --git a/web/src/pages/user-setting/setting-model/modal/fish-audio-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/fish-audio-modal/index.tsx index 3fbf406576..368ff5bd0e 100644 --- a/web/src/pages/user-setting/setting-model/modal/fish-audio-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/fish-audio-modal/index.tsx @@ -1,5 +1,6 @@ import { DynamicForm, + DynamicFormRef, FormFieldConfig, FormFieldType, } from '@/components/dynamic-form'; @@ -13,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -34,6 +35,7 @@ const FishAudioModal = ({ const { t } = useTranslate('setting'); const { t: tc } = useCommonTranslation(); const { buildModelTypeOptions } = useBuildModelTypeOptions(); + const formRef = useRef(null); const { instanceNameSet } = useFetchInstanceNameSet(llmFactory); const hideWhenInstanceExists = useHideWhenInstanceExists(instanceNameSet); @@ -125,6 +127,12 @@ const FishAudioModal = ({ [llmFactory, onVerify], ); + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } @@ -137,6 +145,7 @@ const FishAudioModal = ({ console.log(data)} + ref={formRef} defaultValues={{ instance_name: '', model_type: ['tts'], diff --git a/web/src/pages/user-setting/setting-model/modal/google-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/google-modal/index.tsx index 289d995f14..07dd1c8770 100644 --- a/web/src/pages/user-setting/setting-model/modal/google-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/google-modal/index.tsx @@ -1,5 +1,6 @@ import { DynamicForm, + DynamicFormRef, FormFieldConfig, FormFieldType, } from '@/components/dynamic-form'; @@ -13,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -35,6 +36,7 @@ const GoogleModal = ({ const { t: tc } = useCommonTranslation(); const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { instanceNameSet } = useFetchInstanceNameSet(llmFactory); + const formRef = useRef(null); const hideWhenInstanceExists = useHideWhenInstanceExists(instanceNameSet); @@ -155,6 +157,13 @@ const GoogleModal = ({ }, [verifyParamsFunc, onVerify], ); + + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } @@ -168,6 +177,7 @@ const GoogleModal = ({ onSubmit={() => { // Form submission is handled by SavingButton }} + ref={formRef} defaultValues={ { instance_name: '', diff --git a/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx index cc94eb40a6..e3d15ebbf6 100644 --- a/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/mineru-modal/index.tsx @@ -17,7 +17,7 @@ import { VerifyResult } from '@/pages/user-setting/setting-model/hooks'; import { buildOptions } from '@/utils/form'; import { zodResolver } from '@hookform/resolvers/zod'; import { t } from 'i18next'; -import { memo } from 'react'; +import { memo, useEffect } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -92,6 +92,12 @@ const MinerUModal = ({ } }; + useEffect(() => { + if (!visible) { + form.reset(); + } + }, [visible, form]); + return ( diff --git a/web/src/pages/user-setting/setting-model/modal/next-tencent-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/next-tencent-modal/index.tsx index e3c6592157..f95d3ef1d2 100644 --- a/web/src/pages/user-setting/setting-model/modal/next-tencent-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/next-tencent-modal/index.tsx @@ -1,5 +1,6 @@ import { DynamicForm, + DynamicFormRef, FormFieldConfig, FormFieldType, } from '@/components/dynamic-form'; @@ -13,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -34,6 +35,7 @@ const TencentCloudModal = ({ const { t } = useTranslate('setting'); const { t: tc } = useCommonTranslation(); const { buildModelTypeOptions } = useBuildModelTypeOptions(); + const formRef = useRef(null); const { instanceNameSet } = useFetchInstanceNameSet(llmFactory); const hideWhenInstanceExists = useHideWhenInstanceExists(instanceNameSet); @@ -147,6 +149,12 @@ const TencentCloudModal = ({ [verifyParamsFunc, onVerify], ); + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } @@ -158,6 +166,7 @@ const TencentCloudModal = ({ {}} + ref={formRef} defaultValues={ { instance_name: '', diff --git a/web/src/pages/user-setting/setting-model/modal/opendataloader-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/opendataloader-modal/index.tsx index 7d3c6e7933..7b919aeb65 100644 --- a/web/src/pages/user-setting/setting-model/modal/opendataloader-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/opendataloader-modal/index.tsx @@ -12,7 +12,7 @@ import { Input } from '@/components/ui/input'; import { LLMFactory } from '@/constants/llm'; import { VerifyResult } from '@/pages/user-setting/setting-model/hooks'; import { zodResolver } from '@hookform/resolvers/zod'; -import { memo, useMemo } from 'react'; +import { memo, useEffect, useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -78,6 +78,12 @@ const OpenDataLoaderModal = ({ } }; + useEffect(() => { + if (!visible) { + form.reset(); + } + }, [visible, form]); + return ( diff --git a/web/src/pages/user-setting/setting-model/modal/paddleocr-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/paddleocr-modal/index.tsx index 17478756b4..b39fce9434 100644 --- a/web/src/pages/user-setting/setting-model/modal/paddleocr-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/paddleocr-modal/index.tsx @@ -14,7 +14,7 @@ import { LLMFactory } from '@/constants/llm'; import { VerifyResult } from '@/pages/user-setting/setting-model/hooks'; import { zodResolver } from '@hookform/resolvers/zod'; import { t } from 'i18next'; -import { memo } from 'react'; +import { memo, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; @@ -78,6 +78,12 @@ const PaddleOCRModal = ({ } }; + useEffect(() => { + if (!visible) { + form.reset(); + } + }, [visible, form]); + return ( diff --git a/web/src/pages/user-setting/setting-model/modal/spark-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/spark-modal/index.tsx index 6f3648ec78..0314c8d552 100644 --- a/web/src/pages/user-setting/setting-model/modal/spark-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/spark-modal/index.tsx @@ -14,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -182,6 +182,12 @@ const SparkModal = ({ [verifyParamsFunc, onVerify], ); + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } diff --git a/web/src/pages/user-setting/setting-model/modal/volcengine-modal/index.tsx b/web/src/pages/user-setting/setting-model/modal/volcengine-modal/index.tsx index cf6f4426c6..49d0a2a105 100644 --- a/web/src/pages/user-setting/setting-model/modal/volcengine-modal/index.tsx +++ b/web/src/pages/user-setting/setting-model/modal/volcengine-modal/index.tsx @@ -14,7 +14,7 @@ import { useHideWhenInstanceExists, VerifyResult, } from '@/pages/user-setting/setting-model/hooks'; -import { memo, useCallback, useMemo, useRef } from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { FieldValues } from 'react-hook-form'; import { LLMHeader } from '../../components/llm-header'; import VerifyButton from '../../modal/verify-button'; @@ -134,6 +134,12 @@ const VolcEngineModal = ({ [verifyParamsFunc, onVerify], ); + useEffect(() => { + if (!visible) { + formRef.current?.reset(); + } + }, [visible]); + return ( } diff --git a/web/src/services/llm-service.ts b/web/src/services/llm-service.ts index e14a6920df..cd76d06f73 100644 --- a/web/src/services/llm-service.ts +++ b/web/src/services/llm-service.ts @@ -7,6 +7,7 @@ const { listProviders, addProvider, addProviderInstance, + verifyProviderConnection, listProviderInstances, listInstanceModels, showProviderInstance, @@ -40,6 +41,10 @@ const methods = { url: addProviderInstance, method: 'post', }, + verifyProviderConnection: { + url: verifyProviderConnection, + method: 'post', + }, listProviderInstances: { url: listProviderInstances, method: 'get', diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index b1376b55e1..b369d217e0 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -30,6 +30,8 @@ export default { addProvider: `${restAPIv1}/providers/`, addProviderInstance: ({ llm_factory }: { llm_factory: string }) => `${restAPIv1}/providers/${llm_factory}/instances`, + verifyProviderConnection: ({ provider_name }: { provider_name: string }) => + `${restAPIv1}/providers/${provider_name}/connection`, listProviderInstances: ({ provider_name }: { provider_name: string }) => `${restAPIv1}/providers/${provider_name}/instances`, listInstanceModels: ({