Feat: Add model verify (#13005)

### What problem does this PR solve?

Feat: Add model verify

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Liu An <asiro@qq.com>
This commit is contained in:
chanx
2026-02-05 15:53:20 +08:00
committed by GitHub
parent 90b726c988
commit 89fdb1d498
22 changed files with 1083 additions and 220 deletions

View File

@ -77,7 +77,14 @@ if (process.env.NODE_ENV === 'development') {
}, },
); );
} }
const queryClient = new QueryClient(); const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: 2,
},
},
});
type Locale = ConfigProviderProps['locale']; type Locale = ConfigProviderProps['locale'];

View File

@ -119,6 +119,7 @@ export const ConfirmDeleteDialogNode = ({
}) => { }) => {
return ( return (
<div className="flex flex-col gap-2.5"> <div className="flex flex-col gap-2.5">
{(avatar || name) && (
<div className="flex items-center border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4"> <div className="flex items-center border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4">
{avatar && ( {avatar && (
<RAGFlowAvatar <RAGFlowAvatar
@ -130,6 +131,7 @@ export const ConfirmDeleteDialogNode = ({
)} )}
{name && <div className="ml-3">{name}</div>} {name && <div className="ml-3">{name}</div>}
</div> </div>
)}
{warnText && <div className="text-state-error text-xs">{warnText}</div>} {warnText && <div className="text-state-error text-xs">{warnText}</div>}
{children} {children}
</div> </div>

View File

@ -271,11 +271,12 @@ export interface IApiKeySavingParams {
llm_name?: string; llm_name?: string;
model_type?: string; model_type?: string;
base_url?: string; base_url?: string;
verify?: boolean;
} }
export const useSaveApiKey = () => { export const useSaveApiKey = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { t } = useTranslation(); // const { t } = useTranslation();
const { const {
data, data,
isPending: loading, isPending: loading,
@ -285,14 +286,14 @@ export const useSaveApiKey = () => {
mutationFn: async (params: IApiKeySavingParams) => { mutationFn: async (params: IApiKeySavingParams) => {
const { data } = await userService.set_api_key(params); const { data } = await userService.set_api_key(params);
if (data.code === 0) { if (data.code === 0) {
message.success(t('message.modified')); // message.success(t('message.modified'));
queryClient.invalidateQueries({ queryKey: [LLMApiAction.MyLlmList] }); queryClient.invalidateQueries({ queryKey: [LLMApiAction.MyLlmList] });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [LLMApiAction.MyLlmListDetailed], queryKey: [LLMApiAction.MyLlmListDetailed],
}); });
queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] }); queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] });
} }
return data.code; return data;
}, },
}); });
@ -330,25 +331,25 @@ export const useSaveTenantInfo = () => {
export const useAddLlm = () => { export const useAddLlm = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { t } = useTranslation(); // const { t } = useTranslation();
const { const {
data, data,
isPending: loading, isPending: loading,
mutateAsync, mutateAsync,
} = useMutation({ } = useMutation({
mutationKey: [LLMApiAction.AddLlm], mutationKey: [LLMApiAction.AddLlm],
mutationFn: async (params: IAddLlmRequestBody) => { mutationFn: async (params: IAddLlmRequestBody & { verify?: boolean }) => {
const { data } = await userService.add_llm(params); const { data } = await userService.add_llm(params);
if (data.code === 0) { if (data.code === 0 && !params.verify) {
queryClient.invalidateQueries({ queryKey: [LLMApiAction.MyLlmList] }); queryClient.invalidateQueries({ queryKey: [LLMApiAction.MyLlmList] });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [LLMApiAction.MyLlmListDetailed], queryKey: [LLMApiAction.MyLlmListDetailed],
}); });
queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] }); queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] });
queryClient.invalidateQueries({ queryKey: [LLMApiAction.LlmList] }); queryClient.invalidateQueries({ queryKey: [LLMApiAction.LlmList] });
message.success(t('message.modified')); // message.success(t('message.modified'));
} }
return data.code; return data;
}, },
}); });

View File

@ -61,6 +61,7 @@ export default {
tokenPlaceholder: 'e.g. eyJhbGciOiJIUzI1Ni...', tokenPlaceholder: 'e.g. eyJhbGciOiJIUzI1Ni...',
}, },
selected: 'Selected', selected: 'Selected',
seeAll: 'See all',
}, },
login: { login: {
loginTitle: 'Sign in to your account', loginTitle: 'Sign in to your account',
@ -889,6 +890,9 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
deleteSelectedConfirm: 'Delete the selected {count} session(s)?', deleteSelectedConfirm: 'Delete the selected {count} session(s)?',
}, },
setting: { setting: {
Verify: 'Verify',
keyValid: 'Your API key is valid.',
keyInvalid: 'Your API key is invalid.',
deleteModel: 'Delete model', deleteModel: 'Delete model',
bedrockCredentialsHint: bedrockCredentialsHint:
'Tip: Leave Access Key / Secret Key blank to use AWS IAM authentication.', 'Tip: Leave Access Key / Secret Key blank to use AWS IAM authentication.',

View File

@ -56,6 +56,7 @@ export default {
zendeskDescription: '连接 Zendesk同步工单、文章及其他内容。', zendeskDescription: '连接 Zendesk同步工单、文章及其他内容。',
promptPlaceholder: '请输入或使用 / 快速插入变量。', promptPlaceholder: '请输入或使用 / 快速插入变量。',
selected: '已选择', selected: '已选择',
seeAll: '查看全部',
}, },
login: { login: {
loginTitle: '登录账户', loginTitle: '登录账户',
@ -835,6 +836,9 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
deleteSelectedConfirm: '删除选中的 {count} 个会话?', deleteSelectedConfirm: '删除选中的 {count} 个会话?',
}, },
setting: { setting: {
Verify: '验证',
keyValid: '你的 API 密钥有效。',
keyInvalid: '你的 API 密钥无效。',
deleteModel: '删除模型', deleteModel: '删除模型',
modelEmptyTip: '暂无可用模型,<br>请先在右侧面板添加模型。', modelEmptyTip: '暂无可用模型,<br>请先在右侧面板添加模型。',
sourceEmptyTip: '暂未添加任何数据源,请从下方选择一个进行连接。', sourceEmptyTip: '暂未添加任何数据源,请从下方选择一个进行连接。',

View File

@ -115,7 +115,7 @@ export const ModelProviderCard: FC<IModelCardProps> = ({
content={{ content={{
node: ( node: (
<ConfirmDeleteDialogNode> <ConfirmDeleteDialogNode>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2 border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4">
<LlmIcon name={item.name} /> <LlmIcon name={item.name} />
{item.name} {item.name}
</div> </div>

View File

@ -20,31 +20,57 @@ import { ApiKeyPostBody } from '../interface';
import { MinerUFormValues } from './modal/mineru-modal'; import { MinerUFormValues } from './modal/mineru-modal';
type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>; type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>;
export type VerifyResult = {
isValid: boolean | null;
logs: string;
};
export const useSubmitApiKey = () => { export const useSubmitApiKey = () => {
const [savingParams, setSavingParams] = useState<SavingParamsState>( const [savingParams, setSavingParams] = useState<SavingParamsState>(
{} as SavingParamsState, {} as SavingParamsState,
); );
const [editMode, setEditMode] = useState(false); const [editMode, setEditMode] = useState(false);
const { saveApiKey, loading } = useSaveApiKey(); const { saveApiKey } = useSaveApiKey();
const [saveLoading, setSaveLoading] = useState(false);
const { const {
visible: apiKeyVisible, visible: apiKeyVisible,
hideModal: hideApiKeyModal, hideModal: hideApiKeyModal,
showModal: showApiKeyModal, showModal: showApiKeyModal,
} = useSetModalState(); } = useSetModalState();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const onApiKeySavingOk = useCallback( const onApiKeySavingOk = useCallback(
async (postBody: ApiKeyPostBody) => { async (postBody: ApiKeyPostBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await saveApiKey({ const ret = await saveApiKey({
...savingParams, ...savingParams,
...postBody, ...postBody,
verify: isVerify,
}); });
if (!isVerify) {
if (ret === 0) { setSaveLoading(false);
if (ret.code === 0) {
queryClient.invalidateQueries({ queryKey: ['llmList'] }); queryClient.invalidateQueries({ queryKey: ['llmList'] });
hideApiKeyModal(); hideApiKeyModal();
setEditMode(false); 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, saveApiKey, savingParams, queryClient], [hideApiKeyModal, saveApiKey, savingParams, queryClient],
); );
@ -59,7 +85,7 @@ export const useSubmitApiKey = () => {
); );
return { return {
saveApiKeyLoading: loading, saveApiKeyLoading: saveLoading,
initialApiKey: '', initialApiKey: '',
llmFactory: savingParams.llm_factory, llmFactory: savingParams.llm_factory,
editMode, editMode,
@ -119,7 +145,8 @@ export const useSubmitOllama = () => {
const [initialValues, setInitialValues] = useState< const [initialValues, setInitialValues] = useState<
Partial<IAddLlmRequestBody> & { provider_order?: string } Partial<IAddLlmRequestBody> & { provider_order?: string }
>(); >();
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: llmAddingVisible, visible: llmAddingVisible,
hideModal: hideLlmAddingModal, hideModal: hideLlmAddingModal,
@ -127,20 +154,41 @@ export const useSubmitOllama = () => {
} = useSetModalState(); } = useSetModalState();
const onLlmAddingOk = useCallback( const onLlmAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cleanedPayload = { ...payload }; const cleanedPayload = { ...payload };
if (!cleanedPayload.api_key || cleanedPayload.api_key.trim() === '') { if (!cleanedPayload.api_key || cleanedPayload.api_key.trim() === '') {
delete cleanedPayload.api_key; delete cleanedPayload.api_key;
} }
const ret = await addLlm(cleanedPayload); const ret = await addLlm({ ...cleanedPayload, verify: isVerify });
if (ret === 0) { if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideLlmAddingModal(); hideLlmAddingModal();
setEditMode(false); setEditMode(false);
setInitialValues(undefined); setInitialValues(undefined);
} }
}
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;
}
}, },
[hideLlmAddingModal, addLlm], [hideLlmAddingModal, addLlm, setSaveLoading],
); );
const handleShowLlmAddingModal = ( const handleShowLlmAddingModal = (
@ -168,7 +216,7 @@ export const useSubmitOllama = () => {
}; };
return { return {
llmAddingLoading: loading, llmAddingLoading: saveLoading,
editMode, editMode,
initialValues, initialValues,
onLlmAddingOk, onLlmAddingOk,
@ -180,7 +228,8 @@ export const useSubmitOllama = () => {
}; };
export const useSubmitVolcEngine = () => { export const useSubmitVolcEngine = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: volcAddingVisible, visible: volcAddingVisible,
hideModal: hideVolcAddingModal, hideModal: hideVolcAddingModal,
@ -188,17 +237,38 @@ export const useSubmitVolcEngine = () => {
} = useSetModalState(); } = useSetModalState();
const onVolcAddingOk = useCallback( const onVolcAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideVolcAddingModal(); hideVolcAddingModal();
} }
}
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;
}
}, },
[hideVolcAddingModal, addLlm], [hideVolcAddingModal, addLlm, setSaveLoading],
); );
return { return {
volcAddingLoading: loading, volcAddingLoading: saveLoading,
onVolcAddingOk, onVolcAddingOk,
volcAddingVisible, volcAddingVisible,
hideVolcAddingModal, hideVolcAddingModal,
@ -207,7 +277,8 @@ export const useSubmitVolcEngine = () => {
}; };
export const useSubmitTencentCloud = () => { export const useSubmitTencentCloud = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: TencentCloudAddingVisible, visible: TencentCloudAddingVisible,
hideModal: hideTencentCloudAddingModal, hideModal: hideTencentCloudAddingModal,
@ -215,17 +286,38 @@ export const useSubmitTencentCloud = () => {
} = useSetModalState(); } = useSetModalState();
const onTencentCloudAddingOk = useCallback( const onTencentCloudAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideTencentCloudAddingModal(); hideTencentCloudAddingModal();
} }
}
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;
}
}, },
[hideTencentCloudAddingModal, addLlm], [hideTencentCloudAddingModal, addLlm, setSaveLoading],
); );
return { return {
TencentCloudAddingLoading: loading, TencentCloudAddingLoading: saveLoading,
onTencentCloudAddingOk, onTencentCloudAddingOk,
TencentCloudAddingVisible, TencentCloudAddingVisible,
hideTencentCloudAddingModal, hideTencentCloudAddingModal,
@ -234,7 +326,8 @@ export const useSubmitTencentCloud = () => {
}; };
export const useSubmitSpark = () => { export const useSubmitSpark = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: SparkAddingVisible, visible: SparkAddingVisible,
hideModal: hideSparkAddingModal, hideModal: hideSparkAddingModal,
@ -242,17 +335,38 @@ export const useSubmitSpark = () => {
} = useSetModalState(); } = useSetModalState();
const onSparkAddingOk = useCallback( const onSparkAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideSparkAddingModal(); hideSparkAddingModal();
} }
}
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;
}
}, },
[hideSparkAddingModal, addLlm], [hideSparkAddingModal, addLlm, setSaveLoading],
); );
return { return {
SparkAddingLoading: loading, SparkAddingLoading: saveLoading,
onSparkAddingOk, onSparkAddingOk,
SparkAddingVisible, SparkAddingVisible,
hideSparkAddingModal, hideSparkAddingModal,
@ -261,7 +375,8 @@ export const useSubmitSpark = () => {
}; };
export const useSubmityiyan = () => { export const useSubmityiyan = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: yiyanAddingVisible, visible: yiyanAddingVisible,
hideModal: hideyiyanAddingModal, hideModal: hideyiyanAddingModal,
@ -269,17 +384,38 @@ export const useSubmityiyan = () => {
} = useSetModalState(); } = useSetModalState();
const onyiyanAddingOk = useCallback( const onyiyanAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideyiyanAddingModal(); hideyiyanAddingModal();
} }
}
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;
}
}, },
[hideyiyanAddingModal, addLlm], [hideyiyanAddingModal, addLlm, setSaveLoading],
); );
return { return {
yiyanAddingLoading: loading, yiyanAddingLoading: saveLoading,
onyiyanAddingOk, onyiyanAddingOk,
yiyanAddingVisible, yiyanAddingVisible,
hideyiyanAddingModal, hideyiyanAddingModal,
@ -288,7 +424,8 @@ export const useSubmityiyan = () => {
}; };
export const useSubmitFishAudio = () => { export const useSubmitFishAudio = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: FishAudioAddingVisible, visible: FishAudioAddingVisible,
hideModal: hideFishAudioAddingModal, hideModal: hideFishAudioAddingModal,
@ -296,17 +433,38 @@ export const useSubmitFishAudio = () => {
} = useSetModalState(); } = useSetModalState();
const onFishAudioAddingOk = useCallback( const onFishAudioAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideFishAudioAddingModal(); hideFishAudioAddingModal();
} }
}
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;
}
}, },
[hideFishAudioAddingModal, addLlm], [hideFishAudioAddingModal, addLlm, setSaveLoading],
); );
return { return {
FishAudioAddingLoading: loading, FishAudioAddingLoading: saveLoading,
onFishAudioAddingOk, onFishAudioAddingOk,
FishAudioAddingVisible, FishAudioAddingVisible,
hideFishAudioAddingModal, hideFishAudioAddingModal,
@ -315,7 +473,8 @@ export const useSubmitFishAudio = () => {
}; };
export const useSubmitGoogle = () => { export const useSubmitGoogle = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: GoogleAddingVisible, visible: GoogleAddingVisible,
hideModal: hideGoogleAddingModal, hideModal: hideGoogleAddingModal,
@ -323,17 +482,38 @@ export const useSubmitGoogle = () => {
} = useSetModalState(); } = useSetModalState();
const onGoogleAddingOk = useCallback( const onGoogleAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideGoogleAddingModal(); hideGoogleAddingModal();
} }
}
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;
}
}, },
[hideGoogleAddingModal, addLlm], [hideGoogleAddingModal, addLlm, setSaveLoading],
); );
return { return {
GoogleAddingLoading: loading, GoogleAddingLoading: saveLoading,
onGoogleAddingOk, onGoogleAddingOk,
GoogleAddingVisible, GoogleAddingVisible,
hideGoogleAddingModal, hideGoogleAddingModal,
@ -342,7 +522,8 @@ export const useSubmitGoogle = () => {
}; };
export const useSubmitBedrock = () => { export const useSubmitBedrock = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: bedrockAddingVisible, visible: bedrockAddingVisible,
hideModal: hideBedrockAddingModal, hideModal: hideBedrockAddingModal,
@ -350,17 +531,38 @@ export const useSubmitBedrock = () => {
} = useSetModalState(); } = useSetModalState();
const onBedrockAddingOk = useCallback( const onBedrockAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideBedrockAddingModal(); hideBedrockAddingModal();
} }
}
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;
}
}, },
[hideBedrockAddingModal, addLlm], [hideBedrockAddingModal, addLlm, setSaveLoading],
); );
return { return {
bedrockAddingLoading: loading, bedrockAddingLoading: saveLoading,
onBedrockAddingOk, onBedrockAddingOk,
bedrockAddingVisible, bedrockAddingVisible,
hideBedrockAddingModal, hideBedrockAddingModal,
@ -369,7 +571,8 @@ export const useSubmitBedrock = () => {
}; };
export const useSubmitAzure = () => { export const useSubmitAzure = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: AzureAddingVisible, visible: AzureAddingVisible,
hideModal: hideAzureAddingModal, hideModal: hideAzureAddingModal,
@ -377,17 +580,38 @@ export const useSubmitAzure = () => {
} = useSetModalState(); } = useSetModalState();
const onAzureAddingOk = useCallback( const onAzureAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => { async (payload: IAddLlmRequestBody, isVerify = false) => {
const ret = await addLlm(payload); if (!isVerify) {
if (ret === 0) { setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideAzureAddingModal(); hideAzureAddingModal();
} }
}
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;
}
}, },
[hideAzureAddingModal, addLlm], [hideAzureAddingModal, addLlm, setSaveLoading],
); );
return { return {
AzureAddingLoading: loading, AzureAddingLoading: saveLoading,
onAzureAddingOk, onAzureAddingOk,
AzureAddingVisible, AzureAddingVisible,
hideAzureAddingModal, hideAzureAddingModal,
@ -436,7 +660,8 @@ export const useHandleDeleteFactory = (llmFactory: string) => {
}; };
export const useSubmitMinerU = () => { export const useSubmitMinerU = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: mineruVisible, visible: mineruVisible,
hideModal: hideMineruModal, hideModal: hideMineruModal,
@ -444,7 +669,10 @@ export const useSubmitMinerU = () => {
} = useSetModalState(); } = useSetModalState();
const onMineruOk = useCallback( const onMineruOk = useCallback(
async (payload: MinerUFormValues) => { async (payload: MinerUFormValues, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cfg: any = { const cfg: any = {
...payload, ...payload,
mineru_delete_output: mineru_delete_output:
@ -461,12 +689,30 @@ export const useSubmitMinerU = () => {
api_base: '', api_base: '',
max_tokens: 0, max_tokens: 0,
}; };
const ret = await addLlm(req); const ret = await addLlm({ ...req, verify: isVerify });
if (ret === 0) { if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideMineruModal(); hideMineruModal();
} }
}
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;
}
}, },
[addLlm, hideMineruModal], [addLlm, hideMineruModal, setSaveLoading],
); );
return { return {
@ -474,12 +720,13 @@ export const useSubmitMinerU = () => {
hideMineruModal, hideMineruModal,
showMineruModal, showMineruModal,
onMineruOk, onMineruOk,
mineruLoading: loading, mineruLoading: saveLoading,
}; };
}; };
export const useSubmitPaddleOCR = () => { export const useSubmitPaddleOCR = () => {
const { addLlm, loading } = useAddLlm(); const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const { const {
visible: paddleocrVisible, visible: paddleocrVisible,
hideModal: hidePaddleOCRModal, hideModal: hidePaddleOCRModal,
@ -487,7 +734,10 @@ export const useSubmitPaddleOCR = () => {
} = useSetModalState(); } = useSetModalState();
const onPaddleOCROk = useCallback( const onPaddleOCROk = useCallback(
async (payload: any) => { async (payload: any, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cfg: any = { const cfg: any = {
...payload, ...payload,
}; };
@ -499,14 +749,32 @@ export const useSubmitPaddleOCR = () => {
api_base: '', api_base: '',
max_tokens: 0, max_tokens: 0,
}; };
const ret = await addLlm(req); const ret = await addLlm({ ...req, verify: isVerify });
if (ret === 0) { if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hidePaddleOCRModal(); hidePaddleOCRModal();
return true; return true;
} }
}
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;
}
return false; return false;
}, },
[addLlm, hidePaddleOCRModal], [addLlm, hidePaddleOCRModal, setSaveLoading],
); );
return { return {
@ -514,6 +782,37 @@ export const useSubmitPaddleOCR = () => {
hidePaddleOCRModal, hidePaddleOCRModal,
showPaddleOCRModal, showPaddleOCRModal,
onPaddleOCROk, onPaddleOCROk,
paddleocrLoading: loading, paddleocrLoading: saveLoading,
};
};
export const useVerifySettings = ({
onVerify,
}: {
onVerify:
| ((
postBody: ApiKeyPostBody,
isVerify?: boolean,
) => Promise<VerifyResult | undefined>)
| ((
payload: IAddLlmRequestBody,
isVerify?: boolean,
) => Promise<VerifyResult | undefined>)
| ((
payload: MinerUFormValues,
isVerify?: boolean,
) => Promise<VerifyResult | undefined>)
| ((payload: any, isVerify?: boolean) => Promise<boolean | VerifyResult>)
| (() => void);
}) => {
const onApiKeyVerifying = useCallback(
async (postBody: any) => {
const res = await onVerify(postBody, true);
return res;
},
[onVerify],
);
return {
onApiKeyVerifying,
}; };
}; };

View File

@ -20,6 +20,7 @@ import {
useSubmitTencentCloud, useSubmitTencentCloud,
useSubmitVolcEngine, useSubmitVolcEngine,
useSubmityiyan, useSubmityiyan,
useVerifySettings,
} from './hooks'; } from './hooks';
import ApiKeyModal from './modal/api-key-modal'; import ApiKeyModal from './modal/api-key-modal';
import AzureOpenAIModal from './modal/azure-openai-modal'; import AzureOpenAIModal from './modal/azure-openai-modal';
@ -204,6 +205,76 @@ const ModelProviders = () => {
}, },
[showApiKeyModal, showLlmAddingModal, ModalMap, detailedLlmList], [showApiKeyModal, showLlmAddingModal, ModalMap, detailedLlmList],
); );
const handleOk = useMemo(() => {
if (apiKeyVisible) {
return onApiKeySavingOk;
}
if (llmAddingVisible) {
return onLlmAddingOk;
}
if (volcAddingVisible) {
return onVolcAddingOk;
}
if (TencentCloudAddingVisible) {
return onTencentCloudAddingOk;
}
if (SparkAddingVisible) {
return onSparkAddingOk;
}
if (yiyanAddingVisible) {
return onyiyanAddingOk;
}
if (FishAudioAddingVisible) {
return onFishAudioAddingOk;
}
if (bedrockAddingVisible) {
return onBedrockAddingOk;
}
if (AzureAddingVisible) {
return onAzureAddingOk;
}
if (mineruVisible) {
return onMineruOk;
}
if (paddleocrVisible) {
return onPaddleOCROk;
}
if (GoogleAddingVisible) {
return onGoogleAddingOk;
}
return () => {};
}, [
GoogleAddingVisible,
onGoogleAddingOk,
apiKeyVisible,
onApiKeySavingOk,
llmAddingVisible,
onLlmAddingOk,
volcAddingVisible,
onVolcAddingOk,
TencentCloudAddingVisible,
onTencentCloudAddingOk,
SparkAddingVisible,
onSparkAddingOk,
yiyanAddingVisible,
onyiyanAddingOk,
FishAudioAddingVisible,
onFishAudioAddingOk,
bedrockAddingVisible,
onBedrockAddingOk,
AzureAddingVisible,
onAzureAddingOk,
mineruVisible,
onMineruOk,
paddleocrVisible,
onPaddleOCROk,
]);
const { onApiKeyVerifying } = useVerifySettings({
onVerify: handleOk,
});
return ( return (
<div className="flex w-full border-[0.5px] border-border-button rounded-lg relative "> <div className="flex w-full border-[0.5px] border-border-button rounded-lg relative ">
<Spotlight /> <Spotlight />
@ -227,6 +298,7 @@ const ModelProviders = () => {
initialValue={initialApiKey} initialValue={initialApiKey}
editMode={editMode} editMode={editMode}
onOk={onApiKeySavingOk} onOk={onApiKeySavingOk}
onVerify={onApiKeyVerifying}
llmFactory={llmFactory} llmFactory={llmFactory}
></ApiKeyModal> ></ApiKeyModal>
{llmAddingVisible && ( {llmAddingVisible && (
@ -238,6 +310,7 @@ const ModelProviders = () => {
editMode={llmEditMode} editMode={llmEditMode}
initialValues={llmInitialValues} initialValues={llmInitialValues}
llmFactory={selectedLlmFactory} llmFactory={selectedLlmFactory}
onVerify={onApiKeyVerifying}
></OllamaModal> ></OllamaModal>
)} )}
<VolcEngineModal <VolcEngineModal
@ -246,6 +319,7 @@ const ModelProviders = () => {
onOk={onVolcAddingOk} onOk={onVolcAddingOk}
loading={volcAddingLoading} loading={volcAddingLoading}
llmFactory={LLMFactory.VolcEngine} llmFactory={LLMFactory.VolcEngine}
onVerify={onApiKeyVerifying}
></VolcEngineModal> ></VolcEngineModal>
<GoogleModal <GoogleModal
visible={GoogleAddingVisible} visible={GoogleAddingVisible}
@ -253,6 +327,7 @@ const ModelProviders = () => {
onOk={onGoogleAddingOk} onOk={onGoogleAddingOk}
loading={GoogleAddingLoading} loading={GoogleAddingLoading}
llmFactory={LLMFactory.GoogleCloud} llmFactory={LLMFactory.GoogleCloud}
onVerify={onApiKeyVerifying}
></GoogleModal> ></GoogleModal>
<TencentCloudModal <TencentCloudModal
visible={TencentCloudAddingVisible} visible={TencentCloudAddingVisible}
@ -260,6 +335,7 @@ const ModelProviders = () => {
onOk={onTencentCloudAddingOk} onOk={onTencentCloudAddingOk}
loading={TencentCloudAddingLoading} loading={TencentCloudAddingLoading}
llmFactory={LLMFactory.TencentCloud} llmFactory={LLMFactory.TencentCloud}
onVerify={onApiKeyVerifying}
></TencentCloudModal> ></TencentCloudModal>
<SparkModal <SparkModal
visible={SparkAddingVisible} visible={SparkAddingVisible}
@ -267,6 +343,7 @@ const ModelProviders = () => {
onOk={onSparkAddingOk} onOk={onSparkAddingOk}
loading={SparkAddingLoading} loading={SparkAddingLoading}
llmFactory={LLMFactory.XunFeiSpark} llmFactory={LLMFactory.XunFeiSpark}
onVerify={onApiKeyVerifying}
></SparkModal> ></SparkModal>
<YiyanModal <YiyanModal
visible={yiyanAddingVisible} visible={yiyanAddingVisible}
@ -274,6 +351,7 @@ const ModelProviders = () => {
onOk={onyiyanAddingOk} onOk={onyiyanAddingOk}
loading={yiyanAddingLoading} loading={yiyanAddingLoading}
llmFactory={LLMFactory.BaiduYiYan} llmFactory={LLMFactory.BaiduYiYan}
onVerify={onApiKeyVerifying}
></YiyanModal> ></YiyanModal>
<FishAudioModal <FishAudioModal
visible={FishAudioAddingVisible} visible={FishAudioAddingVisible}
@ -281,6 +359,7 @@ const ModelProviders = () => {
onOk={onFishAudioAddingOk} onOk={onFishAudioAddingOk}
loading={FishAudioAddingLoading} loading={FishAudioAddingLoading}
llmFactory={LLMFactory.FishAudio} llmFactory={LLMFactory.FishAudio}
onVerify={onApiKeyVerifying}
></FishAudioModal> ></FishAudioModal>
<BedrockModal <BedrockModal
visible={bedrockAddingVisible} visible={bedrockAddingVisible}
@ -288,6 +367,7 @@ const ModelProviders = () => {
onOk={onBedrockAddingOk} onOk={onBedrockAddingOk}
loading={bedrockAddingLoading} loading={bedrockAddingLoading}
llmFactory={LLMFactory.Bedrock} llmFactory={LLMFactory.Bedrock}
onVerify={onApiKeyVerifying}
></BedrockModal> ></BedrockModal>
<AzureOpenAIModal <AzureOpenAIModal
visible={AzureAddingVisible} visible={AzureAddingVisible}
@ -295,18 +375,21 @@ const ModelProviders = () => {
onOk={onAzureAddingOk} onOk={onAzureAddingOk}
loading={AzureAddingLoading} loading={AzureAddingLoading}
llmFactory={LLMFactory.AzureOpenAI} llmFactory={LLMFactory.AzureOpenAI}
onVerify={onApiKeyVerifying}
></AzureOpenAIModal> ></AzureOpenAIModal>
<MinerUModal <MinerUModal
visible={mineruVisible} visible={mineruVisible}
hideModal={hideMineruModal} hideModal={hideMineruModal}
onOk={onMineruOk} onOk={onMineruOk}
loading={mineruLoading} loading={mineruLoading}
onVerify={onApiKeyVerifying}
></MinerUModal> ></MinerUModal>
<PaddleOCRModal <PaddleOCRModal
visible={paddleocrVisible} visible={paddleocrVisible}
hideModal={hidePaddleOCRModal} hideModal={hidePaddleOCRModal}
onOk={onPaddleOCROk} onOk={onPaddleOCROk}
loading={paddleocrLoading} loading={paddleocrLoading}
onVerify={onApiKeyVerifying}
></PaddleOCRModal> ></PaddleOCRModal>
</div> </div>
); );

View File

@ -15,6 +15,8 @@ import { KeyboardEventHandler, useCallback, useEffect } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { ApiKeyPostBody } from '../../../interface'; import { ApiKeyPostBody } from '../../../interface';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import { VerifyResult } from '../../hooks';
import VerifyButton from '../verify-button';
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
loading: boolean; loading: boolean;
@ -22,6 +24,9 @@ interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
llmFactory: string; llmFactory: string;
editMode?: boolean; editMode?: boolean;
onOk: (postBody: ApiKeyPostBody) => void; onOk: (postBody: ApiKeyPostBody) => void;
onVerify: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
showModal?(): void; showModal?(): void;
} }
@ -46,6 +51,7 @@ const ApiKeyModal = ({
initialValue, initialValue,
editMode = false, editMode = false,
onOk, onOk,
onVerify,
}: IProps) => { }: IProps) => {
const form = useForm<FieldType>(); const form = useForm<FieldType>();
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
@ -181,6 +187,8 @@ const ApiKeyModal = ({
)} )}
/> />
)} )}
<VerifyButton onVerify={onVerify} />
</div> </div>
</Form> </Form>
</Modal> </Modal>

View File

@ -1,5 +1,6 @@
import { import {
DynamicForm, DynamicForm,
DynamicFormRef,
FormFieldConfig, FormFieldConfig,
FormFieldType, FormFieldType,
} from '@/components/dynamic-form'; } from '@/components/dynamic-form';
@ -8,19 +9,29 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback, useRef } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const AzureOpenAIModal = ({ const AzureOpenAIModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tg } = useCommonTranslation(); const { t: tg } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [ const fields: FormFieldConfig[] = [
{ {
@ -114,6 +125,27 @@ const AzureOpenAIModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
const values = formRef.current?.getValues();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
return {
llm_factory: llmFactory,
model_type: modelType,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -127,6 +159,7 @@ const AzureOpenAIModal = ({
onSubmit={(data) => { onSubmit={(data) => {
console.log(data); console.log(data);
}} }}
ref={formRef}
defaultValues={ defaultValues={
{ {
model_type: 'embedding', model_type: 'embedding',
@ -137,6 +170,8 @@ const AzureOpenAIModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
<>
{onVerify && <VerifyButton onVerify={handleVerify} />}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
<DynamicForm.CancelButton <DynamicForm.CancelButton
handleCancel={() => { handleCancel={() => {
@ -151,9 +186,10 @@ const AzureOpenAIModal = ({
}} }}
/> />
</div> </div>
</>
</DynamicForm.Root> </DynamicForm.Root>
</Modal> </Modal>
); );
}; };
export default AzureOpenAIModal; export default memo(AzureOpenAIModal);

View File

@ -9,12 +9,14 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import { BedrockRegionList } from '../../constant'; import { BedrockRegionList } from '../../constant';
import VerifyButton from '../../modal/verify-button';
type FieldType = IAddLlmRequestBody & { type FieldType = IAddLlmRequestBody & {
auth_mode?: 'access_key_secret' | 'iam_role' | 'assume_role'; auth_mode?: 'access_key_secret' | 'iam_role' | 'assume_role';
@ -28,9 +30,15 @@ const BedrockModal = ({
visible = false, visible = false,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: ct } = useCommonTranslation(); const { t: ct } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -130,6 +138,40 @@ const BedrockModal = ({
onOk?.(data as unknown as IAddLlmRequestBody); onOk?.(data as unknown as IAddLlmRequestBody);
}; };
const verifyParamsFunc = useCallback(() => {
const values = form.getValues();
const cleanedValues: Record<string, any> = { ...values };
const fieldsByMode: Record<string, string[]> = {
access_key_secret: ['bedrock_ak', 'bedrock_sk'],
iam_role: ['aws_role_arn'],
assume_role: [],
};
cleanedValues.auth_mode = authMode;
Object.keys(fieldsByMode).forEach((mode) => {
if (mode !== authMode) {
fieldsByMode[mode].forEach((field) => {
delete cleanedValues[field];
});
}
});
return {
...cleanedValues,
llm_factory: llmFactory,
max_tokens: values.max_tokens,
};
}, [llmFactory, authMode, form]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -262,10 +304,11 @@ const BedrockModal = ({
/> />
)} )}
</RAGFlowFormItem> </RAGFlowFormItem>
{onVerify && <VerifyButton onVerify={handleVerify} />}
</form> </form>
</Form> </Form>
</Modal> </Modal>
); );
}; };
export default BedrockModal; export default memo(BedrockModal);

View File

@ -8,16 +8,25 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const FishAudioModal = ({ const FishAudioModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -85,6 +94,14 @@ const FishAudioModal = ({
await onOk?.(data as IAddLlmRequestBody); await onOk?.(data as IAddLlmRequestBody);
}; };
const handleVerify = useCallback(
async (params: any) => {
const res = await onVerify?.({ ...params, llm_factory: llmFactory });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[llmFactory, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -100,6 +117,9 @@ const FishAudioModal = ({
defaultValues={{ model_type: 'tts' }} defaultValues={{ model_type: 'tts' }}
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="flex items-center justify-between w-full"> <div className="flex items-center justify-between w-full">
<a <a
href="https://fish.audio" href="https://fish.audio"
@ -123,4 +143,4 @@ const FishAudioModal = ({
); );
}; };
export default FishAudioModal; export default memo(FishAudioModal);

View File

@ -8,16 +8,25 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const GoogleModal = ({ const GoogleModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -112,6 +121,20 @@ const GoogleModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
return {
llm_factory: llmFactory,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -132,6 +155,7 @@ const GoogleModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && <VerifyButton onVerify={handleVerify} />}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
<DynamicForm.CancelButton <DynamicForm.CancelButton
handleCancel={() => { handleCancel={() => {
@ -151,4 +175,4 @@ const GoogleModal = ({
); );
}; };
export default GoogleModal; export default memo(GoogleModal);

View File

@ -13,13 +13,16 @@ import { RAGFlowSelect } from '@/components/ui/select';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { LLMFactory } from '@/constants/llm'; import { LLMFactory } from '@/constants/llm';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { buildOptions } from '@/utils/form'; import { buildOptions } from '@/utils/form';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next'; import { t } from 'i18next';
import { memo } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../verify-button';
const FormSchema = z.object({ const FormSchema = z.object({
llm_name: z.string().min(1, { llm_name: z.string().min(1, {
@ -46,8 +49,13 @@ const MinerUModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
}: IModalProps<MinerUFormValues>) => { }: IModalProps<MinerUFormValues> & {
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const backendOptions = buildOptions([ const backendOptions = buildOptions([
@ -152,16 +160,23 @@ const MinerUModal = ({
/> />
)} )}
</RAGFlowFormItem> </RAGFlowFormItem>
{onVerify && (
<VerifyButton
onVerify={onVerify as (postBody: any) => Promise<VerifyResult>}
/>
)}
</form> </form>
</Form> </Form>
<DialogFooter> <DialogFooter>
<div className="flex gap-2">
<ButtonLoading type="submit" form="mineru-form" loading={loading}> <ButtonLoading type="submit" form="mineru-form" loading={loading}>
{t('common.save', 'Save')} {t('common.save', 'Save')}
</ButtonLoading> </ButtonLoading>
</div>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );
}; };
export default MinerUModal; export default memo(MinerUModal);

View File

@ -8,17 +8,24 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const TencentCloudModal = ({ const TencentCloudModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<Omit<IAddLlmRequestBody, 'max_tokens'>> & { }: IModalProps<Omit<IAddLlmRequestBody, 'max_tokens'>> & {
llmFactory: string; llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => { }) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
@ -108,6 +115,21 @@ const TencentCloudModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
return {
llm_factory: llmFactory,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -127,6 +149,9 @@ const TencentCloudModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-between w-full py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-between w-full py-6 px-6">
<a <a
href="https://cloud.tencent.com/document/api/1093/37823" href="https://cloud.tencent.com/document/api/1093/37823"
@ -156,4 +181,4 @@ const TencentCloudModal = ({
); );
}; };
export default TencentCloudModal; export default memo(TencentCloudModal);

View File

@ -1,5 +1,6 @@
import { import {
DynamicForm, DynamicForm,
DynamicFormRef,
FormFieldConfig, FormFieldConfig,
FormFieldType, FormFieldType,
} from '@/components/dynamic-form'; } from '@/components/dynamic-form';
@ -9,9 +10,11 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { useMemo } from 'react'; import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback, useMemo, useRef } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const llmFactoryToUrlMap: Partial<Record<LLMFactory, string>> = { const llmFactoryToUrlMap: Partial<Record<LLMFactory, string>> = {
[LLMFactory.Ollama]: [LLMFactory.Ollama]:
@ -38,6 +41,7 @@ const OllamaModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
editMode = false, editMode = false,
@ -45,10 +49,14 @@ const OllamaModal = ({
}: IModalProps<Partial<IAddLlmRequestBody> & { provider_order?: string }> & { }: IModalProps<Partial<IAddLlmRequestBody> & { provider_order?: string }> & {
llmFactory: string; llmFactory: string;
editMode?: boolean; editMode?: boolean;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => { }) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const optionsMap: Partial< const optionsMap: Partial<
Record<LLMFactory, { label: string; value: string }[]> Record<LLMFactory, { label: string; value: string }[]>
@ -233,6 +241,27 @@ const OllamaModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
const values = formRef.current?.getValues();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
return {
llm_factory: llmFactory,
model_type: modelType,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -245,10 +274,14 @@ const OllamaModal = ({
<DynamicForm.Root <DynamicForm.Root
key={`${visible}-${llmFactory}`} key={`${visible}-${llmFactory}`}
fields={fields} fields={fields}
ref={formRef}
onSubmit={() => {}} onSubmit={() => {}}
defaultValues={defaultValues} defaultValues={defaultValues}
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="flex items-center justify-between w-full gap-2 "> <div className="flex items-center justify-between w-full gap-2 ">
<a href={url} target="_blank" rel="noreferrer" className="text-sm"> <a href={url} target="_blank" rel="noreferrer" className="text-sm">
{t('ollamaLink', { name: llmFactory })} {t('ollamaLink', { name: llmFactory })}
@ -273,4 +306,4 @@ const OllamaModal = ({
); );
}; };
export default OllamaModal; export default memo(OllamaModal);

View File

@ -2,6 +2,7 @@ import { RAGFlowFormItem } from '@/components/ragflow-form';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
@ -9,12 +10,15 @@ import { Form } from '@/components/ui/form';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select'; import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select';
import { LLMFactory } from '@/constants/llm'; import { LLMFactory } from '@/constants/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next'; import { t } from 'i18next';
import { memo } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../verify-button';
const FormSchema = z.object({ const FormSchema = z.object({
llm_name: z.string().min(1, { llm_name: z.string().min(1, {
@ -33,6 +37,9 @@ export interface IModalProps<T> {
visible: boolean; visible: boolean;
hideModal: () => void; hideModal: () => void;
onOk?: (data: T) => Promise<boolean>; onOk?: (data: T) => Promise<boolean>;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
loading?: boolean; loading?: boolean;
} }
@ -44,6 +51,7 @@ const PaddleOCRModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
}: IModalProps<PaddleOCRFormValues>) => { }: IModalProps<PaddleOCRFormValues>) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -113,6 +121,12 @@ const PaddleOCRModal = ({
/> />
)} )}
</RAGFlowFormItem> </RAGFlowFormItem>
{onVerify && (
<VerifyButton
onVerify={onVerify as (postBody: any) => Promise<VerifyResult>}
/>
)}
<DialogFooter>
<div className="flex justify-end space-x-2"> <div className="flex justify-end space-x-2">
<button <button
type="button" type="button"
@ -129,6 +143,7 @@ const PaddleOCRModal = ({
{t('common.add')} {t('common.add')}
</button> </button>
</div> </div>
</DialogFooter>
</form> </form>
</Form> </Form>
</DialogContent> </DialogContent>
@ -136,4 +151,4 @@ const PaddleOCRModal = ({
); );
}; };
export default PaddleOCRModal; export default memo(PaddleOCRModal);

View File

@ -1,5 +1,6 @@
import { import {
DynamicForm, DynamicForm,
DynamicFormRef,
FormFieldConfig, FormFieldConfig,
FormFieldType, FormFieldType,
} from '@/components/dynamic-form'; } from '@/components/dynamic-form';
@ -8,21 +9,30 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { memo, useCallback, useRef } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const SparkModal = ({ const SparkModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [ const fields: FormFieldConfig[] = [
{ {
name: 'model_type', name: 'model_type',
@ -128,6 +138,27 @@ const SparkModal = ({
await onOk?.(data as IAddLlmRequestBody); await onOk?.(data as IAddLlmRequestBody);
}; };
const verifyParamsFunc = useCallback(() => {
const values = formRef.current?.getValues();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
return {
llm_factory: llmFactory,
model_type: modelType,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -141,6 +172,7 @@ const SparkModal = ({
onSubmit={(data) => { onSubmit={(data) => {
console.log(data); console.log(data);
}} }}
ref={formRef}
defaultValues={ defaultValues={
{ {
model_type: 'chat', model_type: 'chat',
@ -149,6 +181,7 @@ const SparkModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && <VerifyButton onVerify={handleVerify} />}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
<DynamicForm.CancelButton <DynamicForm.CancelButton
handleCancel={() => { handleCancel={() => {
@ -168,4 +201,4 @@ const SparkModal = ({
); );
}; };
export default SparkModal; export default memo(SparkModal);

View File

@ -0,0 +1,123 @@
import { Button } from '@/components/ui/button';
import { useTranslate } from '@/hooks/common-hooks';
import { cn } from '@/lib/utils';
import { replaceText } from '@/pages/dataset/process-log-modal';
import { ApiKeyPostBody } from '@/pages/user-setting/interface';
import { RefreshCcw } from 'lucide-react';
import { memo, useCallback, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { VerifyResult } from '../../hooks';
interface IVerifyButton {
onVerify: (params: any) => Promise<VerifyResult>;
isAbsolute?: boolean;
params?: any;
}
const VerifyButton: React.FC<IVerifyButton> = ({
onVerify,
isAbsolute = true,
params,
}) => {
const { t } = useTranslate('setting');
const [isVerifying, setIsVerifying] = useState(false);
const [verifyResult, setVerifyResult] = useState<VerifyResult | null>(null);
const form = useFormContext();
const onHandleVerify = useCallback(async () => {
const formValid = await form?.trigger();
if (!formValid) {
return;
}
// setVerifyLoading(true);
try {
const values = form.getValues();
const result = await onVerify({
...values,
verify: true,
...params,
} as ApiKeyPostBody & { verify: boolean });
setVerifyResult(result);
} catch (error: any) {
let logs = '';
if (error?.message) {
logs = error.message;
} else if (typeof error === 'string') {
logs = error;
}
setVerifyResult({
isValid: false,
logs: logs,
});
} finally {
// setVerifyLoading(false);
}
}, [form, onVerify, params]);
const handleVerify = async () => {
setVerifyResult({
isValid: null,
logs: '',
});
setIsVerifying(true);
try {
await onHandleVerify();
} catch (error) {
setVerifyResult({
isValid: false,
logs: (error as Error).message || 'Unknown error',
});
} finally {
setIsVerifying(false);
}
};
return (
<div
className={cn(
!isAbsolute || (verifyResult && verifyResult.isValid === false)
? 'flex flex-col gap-5 w-full '
: 'absolute left-6 bottom-6 z-[100]',
)}
>
<div className="flex gap-2 items-center">
<Button
type="button"
onClick={handleVerify}
disabled={isVerifying}
variant={'ghost'}
>
<RefreshCcw
size={14}
className={cn(isVerifying ? 'animate-spin-reverse' : '', '')}
/>
{t('Verify')}
</Button>
{verifyResult && verifyResult.isValid !== null && (
<div
className={`flex items-center gap-2 ${
verifyResult.isValid ? 'text-state-success' : 'text-state-error'
}`}
>
<span>
{verifyResult.isValid ? t('keyValid') : t('keyInvalid')}
</span>
</div>
)}
</div>
{verifyResult && verifyResult.isValid !== null && (
<div className="space-y-2">
{verifyResult.logs && (
<div className="w-full whitespace-pre-line text-wrap bg-bg-card rounded-lg h-fit max-h-[250px] overflow-y-auto scrollbar-auto p-2.5">
{replaceText(verifyResult.logs)}
</div>
)}
</div>
)}
</div>
);
};
export default memo(VerifyButton);

View File

@ -1,5 +1,6 @@
import { import {
DynamicForm, DynamicForm,
DynamicFormRef,
FormFieldConfig, FormFieldConfig,
FormFieldType, FormFieldType,
} from '@/components/dynamic-form'; } from '@/components/dynamic-form';
@ -8,8 +9,11 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback, useRef } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
type VolcEngineLlmRequest = IAddLlmRequestBody & { type VolcEngineLlmRequest = IAddLlmRequestBody & {
endpoint_id: string; endpoint_id: string;
@ -20,13 +24,19 @@ const VolcEngineModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [ const fields: FormFieldConfig[] = [
{ {
name: 'model_type', name: 'model_type',
@ -91,6 +101,27 @@ const VolcEngineModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
const values = formRef.current?.getValues();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
return {
llm_factory: llmFactory,
model_type: modelType,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
@ -104,6 +135,7 @@ const VolcEngineModal = ({
onSubmit={(data) => { onSubmit={(data) => {
console.log(data); console.log(data);
}} }}
ref={formRef}
defaultValues={ defaultValues={
{ {
model_type: 'chat', model_type: 'chat',
@ -112,6 +144,9 @@ const VolcEngineModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-between w-full py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-between w-full py-6 px-6">
<a <a
href="https://www.volcengine.com/docs/82379/1302008" href="https://www.volcengine.com/docs/82379/1302008"
@ -140,4 +175,4 @@ const VolcEngineModal = ({
); );
}; };
export default VolcEngineModal; export default memo(VolcEngineModal);

View File

@ -1,5 +1,6 @@
import { import {
DynamicForm, DynamicForm,
DynamicFormRef,
FormFieldConfig, FormFieldConfig,
FormFieldType, FormFieldType,
} from '@/components/dynamic-form'; } from '@/components/dynamic-form';
@ -8,21 +9,32 @@ import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options'; import { useBuildModelTypeOptions } from '@/hooks/logic-hooks/use-build-options';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm'; import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { memo, useCallback, useMemo, useRef } from 'react';
import { FieldValues } from 'react-hook-form'; import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header'; import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const YiyanModal = ({ const YiyanModal = ({
visible, visible,
hideModal, hideModal,
onOk, onOk,
onVerify,
loading, loading,
llmFactory, llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { }: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting'); const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation(); const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions(); const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [ const fields = useMemo<FormFieldConfig[]>(
() => [
{ {
name: 'model_type', name: 'model_type',
label: t('modelType'), label: t('modelType'),
@ -62,7 +74,9 @@ const YiyanModal = ({
min: 0, min: 0,
}, },
}, },
]; ],
[t, buildModelTypeOptions],
);
const handleOk = async (values?: FieldValues) => { const handleOk = async (values?: FieldValues) => {
if (!values) return; if (!values) return;
@ -88,16 +102,47 @@ const YiyanModal = ({
await onOk?.(data); await onOk?.(data);
}; };
const verifyParamsFunc = useCallback(() => {
const values = formRef.current?.getValues();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
return {
llm_factory: llmFactory,
llm_name: values.llm_name as string,
model_type: modelType,
api_key: {
yiyan_ak: values.yiyan_ak,
yiyan_sk: values.yiyan_sk,
},
max_tokens: values.max_tokens as number,
};
}, [llmFactory]);
const handleVerify = useCallback(
async (params: any) => {
const verifyParams = verifyParamsFunc();
const res = await onVerify?.({ ...params, ...verifyParams });
return (res || { isValid: null, logs: '' }) as VerifyResult;
},
[verifyParamsFunc, onVerify],
);
return ( return (
<Modal <Modal
title={<LLMHeader name={llmFactory} />} title={<LLMHeader name={llmFactory} />}
open={visible || false} open={visible || false}
onOpenChange={(open) => !open && hideModal?.()} onOpenChange={(open) => !open && hideModal?.()}
maskClosable={false} maskClosable={false}
footer={<div className="p-4"></div>} // footer={<div className="p-4"></div>}
footer={<></>}
footerClassName="pb-10"
> >
<DynamicForm.Root <DynamicForm.Root
key={`${visible}-${llmFactory}`}
fields={fields} fields={fields}
ref={formRef}
onSubmit={(data) => { onSubmit={(data) => {
console.log(data); console.log(data);
}} }}
@ -109,6 +154,8 @@ const YiyanModal = ({
} }
labelClassName="font-normal" labelClassName="font-normal"
> >
<div>
{onVerify && <VerifyButton onVerify={handleVerify} />}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6"> <div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
<DynamicForm.CancelButton <DynamicForm.CancelButton
handleCancel={() => { handleCancel={() => {
@ -123,9 +170,10 @@ const YiyanModal = ({
}} }}
/> />
</div> </div>
</div>
</DynamicForm.Root> </DynamicForm.Root>
</Modal> </Modal>
); );
}; };
export default YiyanModal; export default memo(YiyanModal);

View File

@ -211,11 +211,16 @@ module.exports = {
'0%,70%,100%': { opacity: '1' }, '0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' }, '20%,50%': { opacity: '0' },
}, },
'spin-reverse': {
from: { transform: 'rotate(0deg)' },
to: { transform: 'rotate(-360deg)' },
},
}, },
animation: { animation: {
'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out',
'caret-blink': 'caret-blink 1.25s ease-out infinite', 'caret-blink': 'caret-blink 1.25s ease-out infinite',
'spin-reverse': 'spin-reverse 1s linear infinite',
}, },
}, },
}, },