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'];

View File

@ -119,6 +119,7 @@ export const ConfirmDeleteDialogNode = ({
}) => {
return (
<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">
{avatar && (
<RAGFlowAvatar
@ -130,6 +131,7 @@ export const ConfirmDeleteDialogNode = ({
)}
{name && <div className="ml-3">{name}</div>}
</div>
)}
{warnText && <div className="text-state-error text-xs">{warnText}</div>}
{children}
</div>

View File

@ -271,11 +271,12 @@ export interface IApiKeySavingParams {
llm_name?: string;
model_type?: string;
base_url?: string;
verify?: boolean;
}
export const useSaveApiKey = () => {
const queryClient = useQueryClient();
const { t } = useTranslation();
// const { t } = useTranslation();
const {
data,
isPending: loading,
@ -285,14 +286,14 @@ export const useSaveApiKey = () => {
mutationFn: async (params: IApiKeySavingParams) => {
const { data } = await userService.set_api_key(params);
if (data.code === 0) {
message.success(t('message.modified'));
// message.success(t('message.modified'));
queryClient.invalidateQueries({ queryKey: [LLMApiAction.MyLlmList] });
queryClient.invalidateQueries({
queryKey: [LLMApiAction.MyLlmListDetailed],
});
queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] });
}
return data.code;
return data;
},
});
@ -330,25 +331,25 @@ export const useSaveTenantInfo = () => {
export const useAddLlm = () => {
const queryClient = useQueryClient();
const { t } = useTranslation();
// const { t } = useTranslation();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [LLMApiAction.AddLlm],
mutationFn: async (params: IAddLlmRequestBody) => {
mutationFn: async (params: IAddLlmRequestBody & { verify?: boolean }) => {
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.MyLlmListDetailed],
});
queryClient.invalidateQueries({ queryKey: [LLMApiAction.FactoryList] });
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...',
},
selected: 'Selected',
seeAll: 'See all',
},
login: {
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)?',
},
setting: {
Verify: 'Verify',
keyValid: 'Your API key is valid.',
keyInvalid: 'Your API key is invalid.',
deleteModel: 'Delete model',
bedrockCredentialsHint:
'Tip: Leave Access Key / Secret Key blank to use AWS IAM authentication.',

View File

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

View File

@ -115,7 +115,7 @@ export const ModelProviderCard: FC<IModelCardProps> = ({
content={{
node: (
<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} />
{item.name}
</div>

View File

@ -20,31 +20,57 @@ import { ApiKeyPostBody } from '../interface';
import { MinerUFormValues } from './modal/mineru-modal';
type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>;
export type VerifyResult = {
isValid: boolean | null;
logs: string;
};
export const useSubmitApiKey = () => {
const [savingParams, setSavingParams] = useState<SavingParamsState>(
{} as SavingParamsState,
);
const [editMode, setEditMode] = useState(false);
const { saveApiKey, loading } = useSaveApiKey();
const { saveApiKey } = useSaveApiKey();
const [saveLoading, setSaveLoading] = useState(false);
const {
visible: apiKeyVisible,
hideModal: hideApiKeyModal,
showModal: showApiKeyModal,
} = useSetModalState();
const queryClient = useQueryClient();
const onApiKeySavingOk = useCallback(
async (postBody: ApiKeyPostBody) => {
async (postBody: ApiKeyPostBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await saveApiKey({
...savingParams,
...postBody,
verify: isVerify,
});
if (ret === 0) {
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
queryClient.invalidateQueries({ queryKey: ['llmList'] });
hideApiKeyModal();
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],
);
@ -59,7 +85,7 @@ export const useSubmitApiKey = () => {
);
return {
saveApiKeyLoading: loading,
saveApiKeyLoading: saveLoading,
initialApiKey: '',
llmFactory: savingParams.llm_factory,
editMode,
@ -119,7 +145,8 @@ export const useSubmitOllama = () => {
const [initialValues, setInitialValues] = useState<
Partial<IAddLlmRequestBody> & { provider_order?: string }
>();
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: llmAddingVisible,
hideModal: hideLlmAddingModal,
@ -127,20 +154,41 @@ export const useSubmitOllama = () => {
} = useSetModalState();
const onLlmAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cleanedPayload = { ...payload };
if (!cleanedPayload.api_key || cleanedPayload.api_key.trim() === '') {
delete cleanedPayload.api_key;
}
const ret = await addLlm(cleanedPayload);
if (ret === 0) {
const ret = await addLlm({ ...cleanedPayload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hideLlmAddingModal();
setEditMode(false);
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 = (
@ -168,7 +216,7 @@ export const useSubmitOllama = () => {
};
return {
llmAddingLoading: loading,
llmAddingLoading: saveLoading,
editMode,
initialValues,
onLlmAddingOk,
@ -180,7 +228,8 @@ export const useSubmitOllama = () => {
};
export const useSubmitVolcEngine = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: volcAddingVisible,
hideModal: hideVolcAddingModal,
@ -188,17 +237,38 @@ export const useSubmitVolcEngine = () => {
} = useSetModalState();
const onVolcAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
volcAddingLoading: loading,
volcAddingLoading: saveLoading,
onVolcAddingOk,
volcAddingVisible,
hideVolcAddingModal,
@ -207,7 +277,8 @@ export const useSubmitVolcEngine = () => {
};
export const useSubmitTencentCloud = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: TencentCloudAddingVisible,
hideModal: hideTencentCloudAddingModal,
@ -215,17 +286,38 @@ export const useSubmitTencentCloud = () => {
} = useSetModalState();
const onTencentCloudAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
TencentCloudAddingLoading: loading,
TencentCloudAddingLoading: saveLoading,
onTencentCloudAddingOk,
TencentCloudAddingVisible,
hideTencentCloudAddingModal,
@ -234,7 +326,8 @@ export const useSubmitTencentCloud = () => {
};
export const useSubmitSpark = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: SparkAddingVisible,
hideModal: hideSparkAddingModal,
@ -242,17 +335,38 @@ export const useSubmitSpark = () => {
} = useSetModalState();
const onSparkAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
SparkAddingLoading: loading,
SparkAddingLoading: saveLoading,
onSparkAddingOk,
SparkAddingVisible,
hideSparkAddingModal,
@ -261,7 +375,8 @@ export const useSubmitSpark = () => {
};
export const useSubmityiyan = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: yiyanAddingVisible,
hideModal: hideyiyanAddingModal,
@ -269,17 +384,38 @@ export const useSubmityiyan = () => {
} = useSetModalState();
const onyiyanAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
yiyanAddingLoading: loading,
yiyanAddingLoading: saveLoading,
onyiyanAddingOk,
yiyanAddingVisible,
hideyiyanAddingModal,
@ -288,7 +424,8 @@ export const useSubmityiyan = () => {
};
export const useSubmitFishAudio = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: FishAudioAddingVisible,
hideModal: hideFishAudioAddingModal,
@ -296,17 +433,38 @@ export const useSubmitFishAudio = () => {
} = useSetModalState();
const onFishAudioAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
FishAudioAddingLoading: loading,
FishAudioAddingLoading: saveLoading,
onFishAudioAddingOk,
FishAudioAddingVisible,
hideFishAudioAddingModal,
@ -315,7 +473,8 @@ export const useSubmitFishAudio = () => {
};
export const useSubmitGoogle = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: GoogleAddingVisible,
hideModal: hideGoogleAddingModal,
@ -323,17 +482,38 @@ export const useSubmitGoogle = () => {
} = useSetModalState();
const onGoogleAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
GoogleAddingLoading: loading,
GoogleAddingLoading: saveLoading,
onGoogleAddingOk,
GoogleAddingVisible,
hideGoogleAddingModal,
@ -342,7 +522,8 @@ export const useSubmitGoogle = () => {
};
export const useSubmitBedrock = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: bedrockAddingVisible,
hideModal: hideBedrockAddingModal,
@ -350,17 +531,38 @@ export const useSubmitBedrock = () => {
} = useSetModalState();
const onBedrockAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
bedrockAddingLoading: loading,
bedrockAddingLoading: saveLoading,
onBedrockAddingOk,
bedrockAddingVisible,
hideBedrockAddingModal,
@ -369,7 +571,8 @@ export const useSubmitBedrock = () => {
};
export const useSubmitAzure = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: AzureAddingVisible,
hideModal: hideAzureAddingModal,
@ -377,17 +580,38 @@ export const useSubmitAzure = () => {
} = useSetModalState();
const onAzureAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
async (payload: IAddLlmRequestBody, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const ret = await addLlm({ ...payload, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
AzureAddingLoading: loading,
AzureAddingLoading: saveLoading,
onAzureAddingOk,
AzureAddingVisible,
hideAzureAddingModal,
@ -436,7 +660,8 @@ export const useHandleDeleteFactory = (llmFactory: string) => {
};
export const useSubmitMinerU = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: mineruVisible,
hideModal: hideMineruModal,
@ -444,7 +669,10 @@ export const useSubmitMinerU = () => {
} = useSetModalState();
const onMineruOk = useCallback(
async (payload: MinerUFormValues) => {
async (payload: MinerUFormValues, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cfg: any = {
...payload,
mineru_delete_output:
@ -461,12 +689,30 @@ export const useSubmitMinerU = () => {
api_base: '',
max_tokens: 0,
};
const ret = await addLlm(req);
if (ret === 0) {
const ret = await addLlm({ ...req, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
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 {
@ -474,12 +720,13 @@ export const useSubmitMinerU = () => {
hideMineruModal,
showMineruModal,
onMineruOk,
mineruLoading: loading,
mineruLoading: saveLoading,
};
};
export const useSubmitPaddleOCR = () => {
const { addLlm, loading } = useAddLlm();
const [saveLoading, setSaveLoading] = useState(false);
const { addLlm } = useAddLlm();
const {
visible: paddleocrVisible,
hideModal: hidePaddleOCRModal,
@ -487,7 +734,10 @@ export const useSubmitPaddleOCR = () => {
} = useSetModalState();
const onPaddleOCROk = useCallback(
async (payload: any) => {
async (payload: any, isVerify = false) => {
if (!isVerify) {
setSaveLoading(true);
}
const cfg: any = {
...payload,
};
@ -499,14 +749,32 @@ export const useSubmitPaddleOCR = () => {
api_base: '',
max_tokens: 0,
};
const ret = await addLlm(req);
if (ret === 0) {
const ret = await addLlm({ ...req, verify: isVerify });
if (!isVerify) {
setSaveLoading(false);
if (ret.code === 0) {
hidePaddleOCRModal();
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;
},
[addLlm, hidePaddleOCRModal],
[addLlm, hidePaddleOCRModal, setSaveLoading],
);
return {
@ -514,6 +782,37 @@ export const useSubmitPaddleOCR = () => {
hidePaddleOCRModal,
showPaddleOCRModal,
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,
useSubmitVolcEngine,
useSubmityiyan,
useVerifySettings,
} from './hooks';
import ApiKeyModal from './modal/api-key-modal';
import AzureOpenAIModal from './modal/azure-openai-modal';
@ -204,6 +205,76 @@ const ModelProviders = () => {
},
[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 (
<div className="flex w-full border-[0.5px] border-border-button rounded-lg relative ">
<Spotlight />
@ -227,6 +298,7 @@ const ModelProviders = () => {
initialValue={initialApiKey}
editMode={editMode}
onOk={onApiKeySavingOk}
onVerify={onApiKeyVerifying}
llmFactory={llmFactory}
></ApiKeyModal>
{llmAddingVisible && (
@ -238,6 +310,7 @@ const ModelProviders = () => {
editMode={llmEditMode}
initialValues={llmInitialValues}
llmFactory={selectedLlmFactory}
onVerify={onApiKeyVerifying}
></OllamaModal>
)}
<VolcEngineModal
@ -246,6 +319,7 @@ const ModelProviders = () => {
onOk={onVolcAddingOk}
loading={volcAddingLoading}
llmFactory={LLMFactory.VolcEngine}
onVerify={onApiKeyVerifying}
></VolcEngineModal>
<GoogleModal
visible={GoogleAddingVisible}
@ -253,6 +327,7 @@ const ModelProviders = () => {
onOk={onGoogleAddingOk}
loading={GoogleAddingLoading}
llmFactory={LLMFactory.GoogleCloud}
onVerify={onApiKeyVerifying}
></GoogleModal>
<TencentCloudModal
visible={TencentCloudAddingVisible}
@ -260,6 +335,7 @@ const ModelProviders = () => {
onOk={onTencentCloudAddingOk}
loading={TencentCloudAddingLoading}
llmFactory={LLMFactory.TencentCloud}
onVerify={onApiKeyVerifying}
></TencentCloudModal>
<SparkModal
visible={SparkAddingVisible}
@ -267,6 +343,7 @@ const ModelProviders = () => {
onOk={onSparkAddingOk}
loading={SparkAddingLoading}
llmFactory={LLMFactory.XunFeiSpark}
onVerify={onApiKeyVerifying}
></SparkModal>
<YiyanModal
visible={yiyanAddingVisible}
@ -274,6 +351,7 @@ const ModelProviders = () => {
onOk={onyiyanAddingOk}
loading={yiyanAddingLoading}
llmFactory={LLMFactory.BaiduYiYan}
onVerify={onApiKeyVerifying}
></YiyanModal>
<FishAudioModal
visible={FishAudioAddingVisible}
@ -281,6 +359,7 @@ const ModelProviders = () => {
onOk={onFishAudioAddingOk}
loading={FishAudioAddingLoading}
llmFactory={LLMFactory.FishAudio}
onVerify={onApiKeyVerifying}
></FishAudioModal>
<BedrockModal
visible={bedrockAddingVisible}
@ -288,6 +367,7 @@ const ModelProviders = () => {
onOk={onBedrockAddingOk}
loading={bedrockAddingLoading}
llmFactory={LLMFactory.Bedrock}
onVerify={onApiKeyVerifying}
></BedrockModal>
<AzureOpenAIModal
visible={AzureAddingVisible}
@ -295,18 +375,21 @@ const ModelProviders = () => {
onOk={onAzureAddingOk}
loading={AzureAddingLoading}
llmFactory={LLMFactory.AzureOpenAI}
onVerify={onApiKeyVerifying}
></AzureOpenAIModal>
<MinerUModal
visible={mineruVisible}
hideModal={hideMineruModal}
onOk={onMineruOk}
loading={mineruLoading}
onVerify={onApiKeyVerifying}
></MinerUModal>
<PaddleOCRModal
visible={paddleocrVisible}
hideModal={hidePaddleOCRModal}
onOk={onPaddleOCROk}
loading={paddleocrLoading}
onVerify={onApiKeyVerifying}
></PaddleOCRModal>
</div>
);

View File

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

View File

@ -1,5 +1,6 @@
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} 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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const AzureOpenAIModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tg } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [
{
@ -114,6 +125,27 @@ const AzureOpenAIModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -127,6 +159,7 @@ const AzureOpenAIModal = ({
onSubmit={(data) => {
console.log(data);
}}
ref={formRef}
defaultValues={
{
model_type: 'embedding',
@ -137,6 +170,8 @@ const AzureOpenAIModal = ({
}
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">
<DynamicForm.CancelButton
handleCancel={() => {
@ -151,9 +186,10 @@ const AzureOpenAIModal = ({
}}
/>
</div>
</>
</DynamicForm.Root>
</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 { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import { zodResolver } from '@hookform/resolvers/zod';
import { useMemo } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header';
import { BedrockRegionList } from '../../constant';
import VerifyButton from '../../modal/verify-button';
type FieldType = IAddLlmRequestBody & {
auth_mode?: 'access_key_secret' | 'iam_role' | 'assume_role';
@ -28,9 +30,15 @@ const BedrockModal = ({
visible = false,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: ct } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -130,6 +138,40 @@ const BedrockModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -262,10 +304,11 @@ const BedrockModal = ({
/>
)}
</RAGFlowFormItem>
{onVerify && <VerifyButton onVerify={handleVerify} />}
</form>
</Form>
</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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const FishAudioModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -85,6 +94,14 @@ const FishAudioModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -100,6 +117,9 @@ const FishAudioModal = ({
defaultValues={{ model_type: 'tts' }}
labelClassName="font-normal"
>
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="flex items-center justify-between w-full">
<a
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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const GoogleModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
@ -112,6 +121,20 @@ const GoogleModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -132,6 +155,7 @@ const GoogleModal = ({
}
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">
<DynamicForm.CancelButton
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 { LLMFactory } from '@/constants/llm';
import { IModalProps } from '@/interfaces/common';
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 { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../verify-button';
const FormSchema = z.object({
llm_name: z.string().min(1, {
@ -46,8 +49,13 @@ const MinerUModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
}: IModalProps<MinerUFormValues>) => {
}: IModalProps<MinerUFormValues> & {
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslation();
const backendOptions = buildOptions([
@ -152,16 +160,23 @@ const MinerUModal = ({
/>
)}
</RAGFlowFormItem>
{onVerify && (
<VerifyButton
onVerify={onVerify as (postBody: any) => Promise<VerifyResult>}
/>
)}
</form>
</Form>
<DialogFooter>
<div className="flex gap-2">
<ButtonLoading type="submit" form="mineru-form" loading={loading}>
{t('common.save', 'Save')}
</ButtonLoading>
</div>
</DialogFooter>
</DialogContent>
</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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const TencentCloudModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<Omit<IAddLlmRequestBody, 'max_tokens'>> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
@ -108,6 +115,21 @@ const TencentCloudModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -127,6 +149,9 @@ const TencentCloudModal = ({
}
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">
<a
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 {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} 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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const llmFactoryToUrlMap: Partial<Record<LLMFactory, string>> = {
[LLMFactory.Ollama]:
@ -38,6 +41,7 @@ const OllamaModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
editMode = false,
@ -45,10 +49,14 @@ const OllamaModal = ({
}: IModalProps<Partial<IAddLlmRequestBody> & { provider_order?: string }> & {
llmFactory: string;
editMode?: boolean;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const optionsMap: Partial<
Record<LLMFactory, { label: string; value: string }[]>
@ -233,6 +241,27 @@ const OllamaModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -245,10 +274,14 @@ const OllamaModal = ({
<DynamicForm.Root
key={`${visible}-${llmFactory}`}
fields={fields}
ref={formRef}
onSubmit={() => {}}
defaultValues={defaultValues}
labelClassName="font-normal"
>
{onVerify && (
<VerifyButton onVerify={handleVerify} isAbsolute={false} />
)}
<div className="flex items-center justify-between w-full gap-2 ">
<a href={url} target="_blank" rel="noreferrer" className="text-sm">
{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 {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
@ -9,12 +10,15 @@ import { Form } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { RAGFlowSelect, RAGFlowSelectOptionType } from '@/components/ui/select';
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 { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../verify-button';
const FormSchema = z.object({
llm_name: z.string().min(1, {
@ -33,6 +37,9 @@ export interface IModalProps<T> {
visible: boolean;
hideModal: () => void;
onOk?: (data: T) => Promise<boolean>;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
loading?: boolean;
}
@ -44,6 +51,7 @@ const PaddleOCRModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
}: IModalProps<PaddleOCRFormValues>) => {
const { t } = useTranslation();
@ -113,6 +121,12 @@ const PaddleOCRModal = ({
/>
)}
</RAGFlowFormItem>
{onVerify && (
<VerifyButton
onVerify={onVerify as (postBody: any) => Promise<VerifyResult>}
/>
)}
<DialogFooter>
<div className="flex justify-end space-x-2">
<button
type="button"
@ -129,6 +143,7 @@ const PaddleOCRModal = ({
{t('common.add')}
</button>
</div>
</DialogFooter>
</form>
</Form>
</DialogContent>
@ -136,4 +151,4 @@ const PaddleOCRModal = ({
);
};
export default PaddleOCRModal;
export default memo(PaddleOCRModal);

View File

@ -1,5 +1,6 @@
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} 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 { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { VerifyResult } from '@/pages/user-setting/setting-model/hooks';
import omit from 'lodash/omit';
import { memo, useCallback, useRef } from 'react';
import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const SparkModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [
{
name: 'model_type',
@ -128,6 +138,27 @@ const SparkModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -141,6 +172,7 @@ const SparkModal = ({
onSubmit={(data) => {
console.log(data);
}}
ref={formRef}
defaultValues={
{
model_type: 'chat',
@ -149,6 +181,7 @@ const SparkModal = ({
}
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">
<DynamicForm.CancelButton
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 {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} 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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
type VolcEngineLlmRequest = IAddLlmRequestBody & {
endpoint_id: string;
@ -20,13 +24,19 @@ const VolcEngineModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [
{
name: 'model_type',
@ -91,6 +101,27 @@ const VolcEngineModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
@ -104,6 +135,7 @@ const VolcEngineModal = ({
onSubmit={(data) => {
console.log(data);
}}
ref={formRef}
defaultValues={
{
model_type: 'chat',
@ -112,6 +144,9 @@ const VolcEngineModal = ({
}
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">
<a
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 {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} 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 { IModalProps } from '@/interfaces/common';
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 { LLMHeader } from '../../components/llm-header';
import VerifyButton from '../../modal/verify-button';
const YiyanModal = ({
visible,
hideModal,
onOk,
onVerify,
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
}: IModalProps<IAddLlmRequestBody> & {
llmFactory: string;
onVerify?: (
postBody: any,
) => Promise<boolean | void | VerifyResult | undefined>;
}) => {
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const { buildModelTypeOptions } = useBuildModelTypeOptions();
const formRef = useRef<DynamicFormRef>(null);
const fields: FormFieldConfig[] = [
const fields = useMemo<FormFieldConfig[]>(
() => [
{
name: 'model_type',
label: t('modelType'),
@ -62,7 +74,9 @@ const YiyanModal = ({
min: 0,
},
},
];
],
[t, buildModelTypeOptions],
);
const handleOk = async (values?: FieldValues) => {
if (!values) return;
@ -88,16 +102,47 @@ const YiyanModal = ({
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 (
<Modal
title={<LLMHeader name={llmFactory} />}
open={visible || false}
onOpenChange={(open) => !open && hideModal?.()}
maskClosable={false}
footer={<div className="p-4"></div>}
// footer={<div className="p-4"></div>}
footer={<></>}
footerClassName="pb-10"
>
<DynamicForm.Root
key={`${visible}-${llmFactory}`}
fields={fields}
ref={formRef}
onSubmit={(data) => {
console.log(data);
}}
@ -109,6 +154,8 @@ const YiyanModal = ({
}
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">
<DynamicForm.CancelButton
handleCancel={() => {
@ -123,9 +170,10 @@ const YiyanModal = ({
}}
/>
</div>
</div>
</DynamicForm.Root>
</Modal>
);
};
export default YiyanModal;
export default memo(YiyanModal);

View File

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