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)
This commit is contained in:
chanx
2026-06-03 11:59:57 +08:00
committed by GitHub
parent 76fc1d547f
commit c41855da81
16 changed files with 160 additions and 27 deletions

View File

@@ -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 {

View File

@@ -24,6 +24,7 @@ export interface IAddProviderRequestBody {
export type IAddProviderInstanceRequestBody = IAddLlmRequestBody & {
instance_name: string;
region?: string;
};
export interface IDeleteProviderInstanceRequestBody {

View File

@@ -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<string>) => {
[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<SavingParamsState>(
{} 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(

View File

@@ -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<HTMLInputElement> = useCallback(
@@ -68,6 +68,8 @@ const ApiKeyModal = ({
useEffect(() => {
if (visible) {
form.setValue('api_key', initialValue);
} else {
form.reset();
}
}, [initialValue, form, visible]);

View File

@@ -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 (
<Modal
title={<LLMHeader name={llmFactory} />}

View File

@@ -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 (
<Modal
title={<LLMHeader name={llmFactory} />}

View File

@@ -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<DynamicFormRef>(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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@@ -137,6 +145,7 @@ const FishAudioModal = ({
<DynamicForm.Root
fields={fields}
onSubmit={(data) => console.log(data)}
ref={formRef}
defaultValues={{
instance_name: '',
model_type: ['tts'],

View File

@@ -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<DynamicFormRef>(null);
const hideWhenInstanceExists = useHideWhenInstanceExists(instanceNameSet);
@@ -155,6 +157,13 @@ const GoogleModal = ({
},
[verifyParamsFunc, onVerify],
);
useEffect(() => {
if (!visible) {
formRef.current?.reset();
}
}, [visible]);
return (
<Modal
title={<LLMHeader name={llmFactory} />}
@@ -168,6 +177,7 @@ const GoogleModal = ({
onSubmit={() => {
// Form submission is handled by SavingButton
}}
ref={formRef}
defaultValues={
{
instance_name: '',

View File

@@ -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 (
<Dialog open={visible} onOpenChange={hideModal}>
<DialogContent>

View File

@@ -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<DynamicFormRef>(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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@@ -158,6 +166,7 @@ const TencentCloudModal = ({
<DynamicForm.Root
fields={fields}
onSubmit={() => {}}
ref={formRef}
defaultValues={
{
instance_name: '',

View File

@@ -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 (
<Dialog open={visible} onOpenChange={hideModal}>
<DialogContent>

View File

@@ -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 (
<Dialog open={visible} onOpenChange={hideModal}>
<DialogContent>

View File

@@ -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 (
<Modal
title={<LLMHeader name={llmFactory} />}

View File

@@ -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 (
<Modal
title={<LLMHeader name={llmFactory} />}

View File

@@ -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',

View File

@@ -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: ({